Testing frontend feature flags with React, Jest, and PostHog
Mar 16, 2023
Combining both testing and feature flags can be a bit tricky. Tests generally check only one variant of the feature flag and leave other code untested. If you want to test the code behind feature flags, you must set up your tests to do so.
To do this, you need to mock the flags to access the other variations. This tutorial shows you how to do that by creating a React app with Jest tests, adding PostHog, then setting up tests that work with feature flags by mocking PostHog.
Creating a React app
First, create a basic React app. Make sure Node is installed then run npx create-react-app
with the name of your app. We named ours flag-test
.
npx create-react-app flag-test
After creating the app, in the newly created flag-test
folder, a test is automatically created in src/App.test.js
that looks like this:
// src/App.test.jsimport { render, screen } from '@testing-library/react';import App from './App';test('renders learn react link', () => {render(<App />);const linkElement = screen.getByText(/learn react/i);expect(linkElement).toBeInTheDocument();});
This test uses Jest, a popular JavaScript testing library. It passes when all the default code is in place and is a test to build on as we build out our app.
cd flag-testnpm test
Adding PostHog
If we’ve created our React app and run our first test, we want to add PostHog. First, we need a PostHog instance (sign up for free). We then need our project API key and instance address from it. Once we have them, in our React app, install posthog-js
:
npm i posthog-js
Next, add the PostHogProvider
to index.js
. This enables access to PostHog throughout your React app.
// src/index.jsimport React from 'react';import ReactDOM from 'react-dom/client';import App from './App';import posthog from 'posthog-js';import { PostHogProvider} from 'posthog-js/react'posthog.init("<ph_project_api_key>",{api_host: "https://us.i.posthog.com",});const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<React.StrictMode><PostHogProvider client={posthog}><App /></PostHogProvider></React.StrictMode>);
With this setup, events are automatically captured, and we can set up our React feature flag.
Setting up our feature flag
In PostHog, go to the "Feature Flags" tab and click the "New feature flag" button. Set the key to test-flag
and the release condition to 100% of users then click "Save."
With the flag created, go to src/App.js
in our React app, import useFeatureFlagEnabled
from posthog-js/react
, and use it to check the test-flag
. We have access to this because we set up the PostHogProvider
earlier. We then conditionally render either a link to PostHog if the flag is enabled or the default "Learn React" link if not. This looks like this:
// src/App.jsimport './App.css';import { useFeatureFlagEnabled } from 'posthog-js/react'function App() {const flagEnabled = useFeatureFlagEnabled('test-flag')return (<div className="App"><header className="App-header">{ flagEnabled ?<aclassName="App-link"href="/"target="_blank"rel="noopener noreferrer">Go to PostHog</a> :<aclassName="App-link"href="https://reactjs.org"target="_blank"rel="noopener noreferrer">Learn React</a>}</header></div>);}export default App;
When we run the app again, the main link on the page changed to "Go to PostHog."
Making our tests and feature flags work together
When we run tests now, it still passes, but only tests part of the code. To test all of it, we must handle feature flags by mocking PostHog. To do this, first, install jest-mock
npm i jest-mock
In src/App.test.js
, mock useFeatureFlagEnabled
. Create a new test where the mocked useFeatureFlagEnabled
function return true
, then checks the "Go to PostHog" version of the flag.
// src/App.test.jsimport { render, screen } from '@testing-library/react';import App from './App';import { useFeatureFlagEnabled } from 'posthog-js/react';jest.mock('posthog-js/react', () => ({useFeatureFlagEnabled: jest.fn(),}));test('renders learn react link', () => {render(<App />);const linkElement = screen.getByText(/learn react/i);expect(linkElement).toBeInTheDocument();});test('renders go to posthog link', () => {useFeatureFlagEnabled.mockReturnValueOnce(true);render(<App />);const linkElement = screen.getByText(/go to posthog/i);expect(linkElement).toBeInTheDocument();});
This tests both variants of the flag. You can use this mocking strategy to test other PostHog methods, components, and code throughout your app.
Further reading
- Master Feature Flags: Best practice, tips and examples
- How to run Experiments without feature flags
- How to do a canary release with feature flags in PostHog