最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to mock the useState hook implementation so that it actually changes the state during testing - Stack Overflow

programmeradmin0浏览0评论

I'm trying to test a component which renders two different sub-components when its internal state changes from false to true: when it's false it renders a button that, if pressed, changes the state from false to true and renders the other one. The other is a form that on submit does the opposite.

I've tried to spy on the useState hook to test if it's actually called. But by mocking the module, the actual setState won't work when I need it in the second part of the test, to test the form that renders after.

This is my component:

import React, { useState } from 'react';

const MyComponent = ({handleChange, handleInput}) => {
     const [state, setState] = useState(false);

     return (
       <div>
         {!state
           ? (
             <button
               data-test="button1"
               type="button"
               onClick={() => setState(true)}
             >
               RenderForm
             </button>
           )
           : (
             <form onSubmit={() => setState(false)}>
               <input type="text" onChange={e => handleChange(e)} />
               <button type="submit">
                 Submit Form
               </button>
               <button type="button" onClick={() => setState(false)}>
                 Go Back
               </button>
             </form>
           )
         }
       </div>
     );
   };
export default MyComponent;

This is my test:

import React from 'react';
import { mount } from 'enzyme';
import MyComponent from './MyComponent';


describe('MyComponent', () => {
    let component;

    const mockChange = jest.fn();
    const mockSubmit = jest.fn();
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, 'useState');
    useStateSpy.mockImplementation(init => [init, setState]);

    beforeEach(() => {
       component = mount(<MyComponent handleChange={mockChange} handleSubmit={mockSubmit}/>);
    });

    afterEach(() => {
      component.unmount();
    });

    it('calls setState when pressing btn', ()=> {
       component
         .find('[data-test="button1"]')
         .simulate('click')
       expect(setState).toHaveBeenCalledWith(true) // passes
    })
    it('calls handleChange when input changes value', () => {
       component
         .find('[data-test="button1"]') //can't be found
         .simulate('click') 
       component
         .find('input')
         .simulate('change', { target: { value: 'blabla' }}) 
       expect(mockChange).toHaveBeenCalled() // doesn't pass  
  })

  });

I know what's the problem, but I don't know how to fix it. Is there a way to mock setState? Or is there a way to split the tests so that they don't interfere with each other?

I'm trying to test a component which renders two different sub-components when its internal state changes from false to true: when it's false it renders a button that, if pressed, changes the state from false to true and renders the other one. The other is a form that on submit does the opposite.

I've tried to spy on the useState hook to test if it's actually called. But by mocking the module, the actual setState won't work when I need it in the second part of the test, to test the form that renders after.

This is my component:

import React, { useState } from 'react';

const MyComponent = ({handleChange, handleInput}) => {
     const [state, setState] = useState(false);

     return (
       <div>
         {!state
           ? (
             <button
               data-test="button1"
               type="button"
               onClick={() => setState(true)}
             >
               RenderForm
             </button>
           )
           : (
             <form onSubmit={() => setState(false)}>
               <input type="text" onChange={e => handleChange(e)} />
               <button type="submit">
                 Submit Form
               </button>
               <button type="button" onClick={() => setState(false)}>
                 Go Back
               </button>
             </form>
           )
         }
       </div>
     );
   };
export default MyComponent;

This is my test:

import React from 'react';
import { mount } from 'enzyme';
import MyComponent from './MyComponent';


describe('MyComponent', () => {
    let component;

    const mockChange = jest.fn();
    const mockSubmit = jest.fn();
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, 'useState');
    useStateSpy.mockImplementation(init => [init, setState]);

    beforeEach(() => {
       component = mount(<MyComponent handleChange={mockChange} handleSubmit={mockSubmit}/>);
    });

    afterEach(() => {
      component.unmount();
    });

    it('calls setState when pressing btn', ()=> {
       component
         .find('[data-test="button1"]')
         .simulate('click')
       expect(setState).toHaveBeenCalledWith(true) // passes
    })
    it('calls handleChange when input changes value', () => {
       component
         .find('[data-test="button1"]') //can't be found
         .simulate('click') 
       component
         .find('input')
         .simulate('change', { target: { value: 'blabla' }}) 
       expect(mockChange).toHaveBeenCalled() // doesn't pass  
  })

  });

I know what's the problem, but I don't know how to fix it. Is there a way to mock setState? Or is there a way to split the tests so that they don't interfere with each other?

Share Improve this question edited Aug 28, 2019 at 13:02 skyboyer 23.7k7 gold badges61 silver badges71 bronze badges asked Aug 28, 2019 at 12:32 Dylanbob211Dylanbob211 1,2541 gold badge12 silver badges15 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 14

You should probably not test the internal implementation (e.g. the state in useState etc), but only test the external functionality (click on button changes output).

This make it easier to test your code, you actually test what you want to test and not how it is implemented and if you change the implementation (rename variable for example) you will get a false negative because the code works fine (does not care for variable name and the correct component will be rendered) but your tests will fail, because you changed the name of a variable for example.

This will make it more cumbersome to fix your tests later if you change the code. If you have a large codebase, you want to know if your code works and not how it is implemented.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论