enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.96k stars 2.01k forks source link

Mock more than one useState inside an it/test block #2510

Closed panchal-krunal closed 2 years ago

panchal-krunal commented 3 years ago

Hi All, I am having a login component with two input boxes and one button. I am trying to simulate button click and validate my input boxes in my functional react component. Below is my code -

Login.js
    const [username, setUsername] = React.useState('')
    const [password, setPassword] = React.useState('')
    const [isError, setIsError] = React.useState(false)

    const onUsernameChange = e => setUsername(e.target.value)
    const onPasswordChange = e => setPassword(e.target.value)

const onSubmitButtonClick = () => {
        if (!username || !password) {
            setIsError(true)
        }
        else if (username === 'admin' && password === 'admin') {
            setIsError(false)
        }

    }

return(
<input type="text" onChange={onUsernameChange} placeholder="Username" value={username}/>
<input type="password" onChange={onPasswordChange} placeholder="Password" value={password}/>
<button data-test="btnSubmit" onClick={onSubmitButtonClick} >Submit<button/>
)

I am trying to simulate button click and test the validation inside onSubmitButtonClick

Login.test.js
test('sets no error if username or password is test/test', () => {
        let setUsername = jest.fn()
        setUsername.mockClear()
        React.useState = () => ['test', setUsername]

        let setPassword = jest.fn()
        setPassword.mockClear()
        React.useState = () => ['test', setPassword]

        let setError = jest.fn()
        setError.mockClear()
        React.useState = () => [false, setError]

        wrapper = setup()

        let button = wrapper.find("[data-test='btnSubmit']");
        button.simulate('click')
        expect(setError).toHaveBeenCalledWith(false)
    })

I am able to run the test, but it is failing, I debugged and found that the last value set in mocking state in testcase is getting set for username and password, how can I set different value for all useState ?

ljharb commented 3 years ago

I'd suggest avoiding simulate - it doesn't reliably simulate anything.

These are the tests you need:

  1. your component renders the 2 inputs, and the 1 button, you expect
  2. each of these 3 form controls has an onClick, that when explicitly invoked with .invoke(), does what you expect.

You do not need to be testing React or the browser itself - just your component, and the resulting onClick props.

panchal-krunal commented 3 years ago

Hi @ljharb can you please give me an example of how to test my component?

I tried the same thing, on button click and checking what is expected, can you please how to achieve it?

ljharb commented 3 years ago

You have wrapper - so you'd have 3 assertions, one for each of the form controls; and then you'd have invoke each form control's onClick and assert on the effects of doing so.

I'm sorry, but I can't write your tests for you.

panchal-krunal commented 3 years ago

Thanks @ljharb will try

AbhishekPragada commented 2 years ago

@panchal-krunal did you write the tests. If yes pls post them I cannot understand how to write them.

deepaktringapps commented 2 years ago

Please provide the example test code to understand about mocking multiple usestate