Modifying the UI when unit testing

Viewed 178

Just curious what would be the most optimal strategy in this case:

  1. Testing Logout functionality (Header with Logout Button)
  2. When that button is clicked, I dispatch(Logout), which changes the state of my redux reducer:

    #action-creator.js

    return { type: LOGOUT };

    #reducers.js

    case LOGOUT: return { ...state, loggedin: false }

In my Header I have basically the following:

const user_state = useSelector(state => state.user)
...
{ user_state.loggedin ? <button>Logout</button> : <button>Login</button> }

So in my tests, among other things, I want to test if the UI changes when the Logout button is clicked:

test('Can logout', () => {

let store = mockStore({
    user: {
        loggedin: true // simulate logged in state to display "Logout"
    }
});

const { getByText } = render(<Provider store={store}><Header/>. 
</Provider>);

fireEvent.click(getByText('Logout'))

// ????

});

Ideally, I want to test expect(getByText("Login")).toBeTruthy(); but, obviously, my mocked state is not going to change... Should I initialize the test with the real store and then somehow update it so when fireEvent is fired, the state actually updates and the Login button is rendered.

1 Answers

You could have this approach :

Login component unit test

Test that the login redux action is dispatched correctly when button is clicked (correct action type, correct parameters), test that visual elements are correct (loading spinner for example). If this is an async action, spy on the action function with Jest utilities :

const loginFunctionSpy = jest.spyOn(Module, 'loginAction');

So UI consequences of a successful login are not tested here, because indeed your store is mocked (and UI rely on the store state). But this is fine in my opinion since you also test your Redux setup, and other components responsible for UI when user is logged in.

Redux

Unit test reducer which is responsible for the login action: store is correclty updated after a successful login (user in store is set), test synchronous and asynchronous actions. Reducers are by design easy to test (as it is more or less a state machine).

Home component (displayed after successful login)

Test that if store contains the logged in user, everything is displayed correctly. Test that if user is not in store, user is redirected to other route (say /login).

Beside of this, you could have a proper E2E test with Cypress for example, testing the whole workflow without any mock.

The main idea is to

  • test parts independantly (components, reducers),
  • test the "plumbing" (redux are correctly dispatched by components, components behave correctly for each store configuration)
  • E2E tests for main use cases to cover integration all all parts.

In my opinion this would be an acceptable strategy (in fact a strategy I use for my tests), but there might be better options or "test holes" in this approach. I'd be glad to debate about this and improve it in fact :).

Related