You can find the first part here: Why testing? Let’s continue writing our Redux tests. The file we are testing is: initialState = { : [ { : , : , : , : }, { : , : , : , : } ], : { : , : [] } } { maxId = todos.reduce( .max(todo.id, maxId), ) maxId + } { (action.type) { : { { ...state, : [ ...state.todos, { : nextTodoId(state.todos), : action.payload, : } ] } } : { { ...state, : state.todos.map( { (todo.id !== action.payload) { todo } { ...todo, : !todo.completed } }) } } : { { ...state, filters: { ...state.filters, status: action.payload } } } : state } } const todos id 1 text 'Do boring stuff' completed false color 'purple' id 2 text 'Write tests!' completed false color 'blue' filters status 'All' colors ( ) function nextTodoId todos const ( ) => maxId, todo Math -1 return 1 export default ( ) function appReducer state = initialState, action switch case 'todos/todoAdded' return todos id text completed false case 'todos/todoToggled' return todos => todo if return return completed case 'filters/statusFilterChanged' return // Copy the whole state // Overwrite the filters value // copy the other filter fields // And replace the status field with the new value default return Like we said earlier testing redux reducers is simple. We need to make sure that the function updates the current state as we expect it to. So in the below case: : { { ...state, : [ ...state.todos, { : nextTodoId(state.todos), : action.payload, : } ] } } case 'todos/todoAdded' return todos id text completed false We want to call appReducer with an initialState object, and a second argument. This one should be an object with type = “todos/todoAdded” and a payload which is whatever string we want. And then make sure that the result contains all previous todo with the one we passed as payload. Let’s have a look at the test: it( , () => { appReducerCall = appReducer( initialState, { : , : }); result = { ...initialState, ...initialState.todos.push({ : , : , : }) }; expect(appReducerCall).toEqual(result) }) 'should add a todo if the action type equals to todos/todoAdded' const type 'todos/todoAdded' payload 'Buy beer' const id 3 text 'Buy beer' completed false And let’s have a quick look at our code coverage: See? Now the todos/todoAdded has full coverage. Notice that our "private" (not exported) function nextTodoId has coverage as well. Check line 2. Some people believe in testing private functions, I don’t. It is an internal implementation and all we care for here is that the id has been increased, and we test for that in the assertion. If you look at the initial state it has two todos, we add a third one and the id should be 3. In the future, someone might rename this internal function but the test will still pass, and that’s what we want. Let’s go to next todos/todoToggled. Have a look at it. : { { ...state, : state.todos.map( { (todo.id !== action.payload) { todo } { ...todo, : !todo.completed } }) } } case 'todos/todoToggled' return todos => todo if return return completed The key is to find the action.payload. So what happens is that we loop through all todos array using the map Array function. Then if the current todo.id element equals to the payload it returns the current todo. In summary, we call appReducer with type set to todos/todoToggled and the payload being an existing id. For this test we might want some initial state: Have a look at the below test: it( , () => { appReducerCall = appReducer( initialState, { : , : } ); result = { ...initialState, ...initialState.todos[ ].completed = }; expect(appReducerCall).toEqual(result) }) 'should change the completed to opposite value when calling todos/todoToggled with existing id' const type 'todos/todoToggled' payload 1 const 0 true And the code coverage: And we’ve got the last case to test! Let’s give it a go-to get our first full 100%! This time we call filters/statusFilterChanged. The only thing that happens is that we override the filter’s status property by passing any payload. Simple! Let’s pass the correct type and then the payload and assert that the new state has our new passed state. it( , () => { appReducerCall = appReducer( initialState, { : , : } ); result = { ...initialState, : { : , : [] } }; expect(appReducerCall).toEqual(result) }) 'should change the filter status of the current state using the payload' const type 'filters/statusFilterChanged' payload 'status' const filters status 'status' colors Now, look at that! Full 100% coverage of our reducer.js code! We are done here, let's move to components. @testing-library/react Now finally let’s start using react-testing-library! To start please run npm i --save-dev @testing-library/react This will give you access to the render and the selectors which the library provides. The problem the library is solving is according to the website: You want to write maintainable tests for your React components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your testbase to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down. It makes sense. It is the same philosophy we used to ‘test’ our private nextTodoId function in redux. We do not care about implementation details, we just want to be sure that our app works when we change something. This philosophy works great with BDD, so Behavior Driven Development. In this approach to testing, we construct our tests according to the story requirements. For example: As a user, I want to disable the button. Our tests should not care what functions we call, we want to make sure that the button gets disabled. So we can change dispatched actions, state, some props but as long as the user is able to disable the button we are happy. Let’s move on! This is the component we will start with: React ; { Provider } ; store EntryComponent { ( <EntryComponent /> ); } App; import from 'react' import from 'react-redux' import from './store/store' import from './EntryComponent' ( ) function App return < = > Provider store {store} </ > Provider export default As we can see this is a basic entry component with a Provider. There is not much to be tested here. But we can use this example to introduce the method. render . It is important to understand that when we test our components, we do not test them as they appear in the browser Jest uses a browser to render our components, but as these are unit tests we only render the currently tested component. So, in this case, we are going to render the whole app what will cause a lot of components to have low coverage Let’s create an App.test.js file in the same directory as the App.js exist. And now we need to import several elements. React ; { render } ; App ; describe(‘App.js’, () => { }) import from 'react' import from '@testing-library/react' import from './App' Pretty straightforward! The test is simple. it( , () => { { container } = render( 'should render the App component' const ); expect(container.firstChild).toBeTruthy(); }) < / > App Let’s have a look at the above. The method returns several constants, one of them is the container which is the result of the render. render We can access several properties of the and one of them is the firstChild which we use to assert that it is truthy. Obviously, it is. container What is interesting is that now we rendered more components which are children to the App.js. Let’s have a look at the coverage. See? We have full App.js coverage, as there is not much to test but we’ve hit some other components. Let’s open the EntryComponent.js and have a look at it. logo ; React, { useState } ; { useSelector, useDispatch } ; ; Todo selectTodos = state.todos; { todos = useSelector(selectTodos); dispatch = useDispatch(); [todo, setTodo] = useState( ); addTodo = { setTodo( ) dispatch({ : , : todo}); } ( <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <div style={{display: 'flex'}}> <input placeholder='Add another todo' onChange={event => setTodo(event.target.value)} value={todo}></input> <button onClick={addTodo} disabled={todo ? false : true}>+</button> </div> <ul> { todos.map((item) => { return (<Todo key={item.text} todo={item.text}/>) }) } </ul> </header> </div> ) } export default EntryComponent import from './logo.svg' import from 'react' import from 'react-redux' import './App.css' import from './Todo' const => state ( ) function EntryComponent const const const '' const => () '' type 'todos/todoAdded' payload return < = > div className "App" This component is more complex. We have an add todo button, a placeholder and we even display all the todos added. We will be able to use a lot more react-testing-library features. Hooray! The setup is all the same as for the App.js but we need to import the correct component. Create a properly named file and let’s copy the test from App.js. Remember to update the imports! it( , () => { { container } = render( 'should render the EntryComponent component' const ); expect(container.firstChild).toBeTruthy(); }) < / > EntryComponent Let’s run and it fails! What just happened? The error we see is could find react-redux context ; please ensure component is wrapped <Provider> not value the in a Do you remember when I mentioned that the render method only renders the imported component? The problem here is that we try to test redux, but there is no store initialized, no Provider, etc. The fix is pretty simple, we have to create a store and wrap, just for tests, the EntryComponent in the provider. Have a look at how this is done (it is very well explained here: ). https://redux.js.org/recipes/writing-tests#connected-components So we need to wrap each test in a Provider passing some state. We could each time create a store, then manually wrap the component but we will follow best practices and the what is suggested above. Create a test-utils.js file and add the below code to it: React { render rtlRender } { createStore } { Provider } reducer {} ) { { } rtlRender(ui, { : Wrapper, ...renderOptions }) } * { render } // test-utils.js import from 'react' import as from '@testing-library/react' import from 'redux' import from 'react-redux' // Import your own reducer import from './store/reducer' ( ), ... } = function render ui, { initialState, store = createStore(reducer, initialState renderOptions ( ) function Wrapper { children } return {children} < = > Provider store {store} </ > Provider return wrapper // re-export everything export from '@testing-library/react' // override render method export Note that we override the default render method with our own which accepts as first argument the tested component and as the second an initialState and wraps our component with the Provider with the state. In the end, we export all off react-testing-library methods. Let’s fix our test now and see how it works! First of all, we do no longer import the render from but now from the we created. ’@testing-library/react’ ‘test-utils.js’ Copy the same initialState we used in the reducer.js. And now the passing test looks like this: it( , () => { { container } = render( 'should render the EntryComponent component' const , { initialState }); expect(container.firstChild).toBeTruthy(); }) < / > EntryComponent And passes! Note the code coverage, let’s have a look! So it seems like we have to add a todo to get the coverage! In React testing library we do not call the internal method directly - you remember the philosophy? We just do what the user would. So we have to fill an input field and then press the Add button and check if the newly added todo is present. In order to find elements the render method returns even more helpers, like , . We can use those to find DOM elements. getByText getByTestId Check out the code below. it( , () => { { getByText, getByPlaceholderText } = render( 'should add a new todo to the list when a new todo is not empty and after pressing the button' const , { initialState } ); fireEvent.change( getByPlaceholderText('Add another todo'), { target: { value: '23' } } ) fireEvent.click(getByText('+')) expect(getByText('23')).toBeInTheDocument(); }) < / > EntryComponent The Add button has got a ‘+’ text so we can use to locate it, and the input field has got a placeholder so we use . getByText getByPlaceholderText In order to fill the field and press a button we use the fireEvent method using fireEvent.change for the input and fireEvent.click for the button respectively. We change the field first and fill it in with a string ‘23’ then we press the button. In the final line, we assert again using getByText that the ‘23’ is in the Document! And our tests pass. Just remember that is not part of the testing library and you need to install and set up ‘jest-dom’. toBeInTheDocument Let’s check out our coverage again. A lot better now! And a 100%! But notice one thing, the button has a disabled state if the todo value is equal to false. Hmm. Should we test it? The code coverage is 100%. You will find out in the next part alongside snapshot testing examples and why to rely on snapshots. We will learn what are spies and how we leverage them in our testing. NOT But to get to the next part you need to like react to this post. See the image above? Find those icons above or below the article, on the right hand-side of each paragraph and click one if you like this article, or if you think it is rubbish... Don't forget to share :) If I get 10 reactions you will get the next part!