简体   繁体   中英

How to set an initial state using the useState hook

I wrote a functional component using the useState hook, the component itself works fine but I'm having trouble testing it. I don't understand how to create a initial state in my component. I've tried multiple ways of mocking useState, none of which worked:

describe('<Component/>', () => {
    beforeEach() = > {
        jest.spyOn(Readt, 'useState').mockImplementation( () => React.useState(['test']));
        //Also tried this:
        //React['useState'] = jest.fn().mockImplementation( () => React.useState(['test']));
    }
    it('should match snapshot', () => {
        expect(wrapper).toMatchSnapshot()
    }
}

I've seen this post too but was unable to get it to work for me

How to set initial state for useState Hook in jest and enzyme?

I've also read that your not supposed to mock out useState in your unit test but then how do you set an initial state for your components if you don't mock it out?

Thanks.

EDIT: In regards to an answer below:

Passing in a prop doesn't change the state I'm trying to test.

I have a button which when clicked (one state) renders an input box which, when the user types in (another state) and presses the okay button (yet another state) creates a button based on the content in the input box.

The props which is passed helps renders a list of buttons but it has no control over what the user types into the input box. I'm trying to set a state in the input box and then simulate the click on the okay button and test that. I'm not trying to test reacts native useState functionality. I'm just trying to use what enzyme already has 'wrapper.setState' but how do I do that with useState?

Ideally I'd like to set up my component, set up the state in which the user has typed into the input box and then test to see if the ok button does what it's supposed to do. Without simulating all the clicks before that.

A component's state is internal to it, and this is an essential part of understanding React components. Once you "grok" that, you can understand that no test should ever try to change it.

but then how do you set an initial state for your components?

You pass a value to your useState hook, inside the component . Your tests should then test your component using that initial state ... they should just pass in props to change it, the same way as your non-test code would.

In fact, even if you somehow could change your initial state (which is impossible AFAIK), your test code should NEVER do anything non-test code does (except to set up testing environments which simulate real ones), since the entire point of making tests is to verify that your non-test code works.

Similarly, you never want to test the internals of anything you're testing, whether it's a React component or anything else.

If you do that, every test you write will break the moment any of those internals change, and a HUGE part of the value of tests is that they let you refactor more easily ( if you don't test internals). Instead, you want to use your testing tools to measure the output of your component (ie. what HTML it generates).

And again, this really isn't even about React. Good software tests of anything don't try and mess with that thing's internals, either to change or measure them. Instead, good tests focus on the externals , because again that's all non-test code every works with, and because conceptually everything you test should be a "black box" to your test code.

EDIT: Response to comment/question edit

Your edit doesn't really change my answer to be honest, except that it reminded me Enzyme does have a way to change state in tests (never using it myself I forgot it existed).

But again, just because something is possible doesn't mean it's a good idea, and again, you want to test the exterior of anything you test (not just React components). The state of a React component is like a variable inside a function: it's internal to that function/component, and something you should avoid testing directly .

What you want to test instead is the exterior piece, and (based on your edit) it seems that isn't props ... but it's still something exterior: the DOM.

So what I would instead recommend doing is (again) the same thing the non-test code is doing: "clicking" on your buttons. Enzyme makes this very easy with its "simulate" method ( https://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html ).

If you just simulate the button sequence that creates the state you want, you'll only be testing externals (ie. testing your code the same way it will be used in production), but you will be able to test whatever circumstances you want.

How about setting a value depending on the environment? Jest sets the environment variable NODE_ENV to 'test':

const inTest = process.env.NODE_ENV === 'test'

[value, setValue] = useState(inTest ? mockValue : defaultValue)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM