check state of a component using react testing library
Asked Answered
S

2

17

I have triggered a change in component using react testing library and I want to check the corresponding change in the state of the component. Is it possible to check the state of the component

Swick answered 15/5, 2020 at 6:46 Comment(1)
The state of a component is an internal implementation detail. What's the behaviour that state change causes? Test through the public API - props, interactions with collaborators and the DOM.Readymade
T
24

Probably a No-Go as this is a direct violation of the principles of React-Testing-Library

Intro

The problem

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.

This solution

The more your tests resemble the way your software is used, the more confidence they can give you.

Basically, it doesn't concern itself with the internal implementation details of a component, like state, but rather its API, i.e. the props and how a user can interact with it. It's more like black-box testing.

Traject answered 15/5, 2020 at 6:52 Comment(5)
Well, this is nice in theory. But in real life you often need to access inner state of component. Example: you want to change position of cursor in draft-js. That is impossible via browser events. you need to use dradt-js API for that. draft-js EditorState is stored as react state within the component. So such picky restrictions cause that we have to have react-testing-library + enzyme in one project. Another option would be not to test that use case.Utimer
Oh, sorry, there is workaround for such restriction. Store that state into global variable that would be used only in test. That is what react-testing-library forces me to do.Utimer
@Utimer I see your point. It's not a theory, but a sound testing practice. It doesn't mean it's a 100% fit-all testing scenario though. RTL is just a different, opinionated, testing mentality.Traject
I know it's not a good practice to make assertions about the internal state, but I'm trying to debug the damned thing and it sure would be nice to be able to see which component doesn't have the internal state that I expected it to have.Emblements
@Emblements Unit testing isn't the same thing as debugging problematic code. You could set a breakpoint near before where you think the issue is and step through the code, or comment out large chunks of the app until the issue goes away and incrementally add code back in a component at-a-time until it breaks and repeat again in that component until you find the offending code, there's always the trusty old console.log debugging, and more.Traject
U
2

Disclaimer: I agree that using inner state of component for testing isn't nice practice. But there are sometimes situations where you are forced to, otherwise use case would not be tested at all.

Example: You want to change position of cursor in draft-js. That is impossible via browser/JSDOM events. You need to use dradt-js API for that. draft-js EditorState is stored as react state within the component. So in order to test this test case you are forced to manipulate inner state of the component.

  1. Option is to use Enzyme APIs state and setState. Yes that means using Enzyme (for such exceptional cases) and react-testing-library in one project

  2. But in my test I was facing also Enzyme issue, which is not worth mentioning, so I ended up doing such nasty trick. In production component itself, I stored state and setter into global variables. I named them so that it is obviously not good idea to use them in production:

    export let innerStateFOR_TESTING;
    export let setInnerStateFOR_TESTING;
    
    const Component = () => {
        const [innerState, setInnerState] = React.useState(null);
        innerStateFOR_TESTING = innerState;
        setInnerStateFOR_TESTING = setInnerState;
    
        ... 
    }
    

    In test I can than render the component, which would fill in those global variables. Than I can manipulate/check inner state for testing purposes.

I wish there would be option in react-testing-library. It can be discouraged practice, but if your other option is not test certain scenario, it would be available.

Utimer answered 10/2, 2022 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.