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

javascript - How to set initial state value for useState Hook in jest and enzyme? - Stack Overflow

programmeradmin1浏览0评论

I know this question is already asked here: How to set initial state for useState Hook in jest and enzyme?

const [state, setState] = useState([]);

And I totally agree with Jimmy's Answer to mock the useState function from test file but I have some extended version of this question, "What if I have multiple useState statements into the hooks, How can I test them and assign the respective custom values to them?"

I have some JSX rendering with the condition of hook's state values and depending on the values of that state the JSX is rendering.

How Can I test those JSX by getting them into the wrapper of my test case code?

I know this question is already asked here: How to set initial state for useState Hook in jest and enzyme?

const [state, setState] = useState([]);

And I totally agree with Jimmy's Answer to mock the useState function from test file but I have some extended version of this question, "What if I have multiple useState statements into the hooks, How can I test them and assign the respective custom values to them?"

I have some JSX rendering with the condition of hook's state values and depending on the values of that state the JSX is rendering.

How Can I test those JSX by getting them into the wrapper of my test case code?

Share Improve this question edited Mar 4, 2020 at 23:51 Michael Peyper 6,9442 gold badges30 silver badges46 bronze badges asked Feb 13, 2020 at 11:43 HarshalHarshal 3,6229 gold badges39 silver badges67 bronze badges 3
  • 2 If the value is set directly, without any external property (like a parent received prop), then I don't think there should be a reason to test different initial values; but I could definitely be wrong about it. Could you share an example of a ponent with a hook like the one you are trying to test? – Alvaro Commented Feb 13, 2020 at 12:25
  • I can provide an example: say your state is updated in a child ponent, but used and defined in the parent ponent you are testing, if you shallow mount the parent there is no way to trigger a state update, so using an initial state could help with covering all parent ponent branches. – Ioan-Radu Tanasescu Commented Jun 4, 2020 at 13:09
  • I would rethink the testing on this situation, if you have a button that fires a setState trigger it, then if you have a paragraph that renders that state, assert it. – alextrastero Commented Oct 20, 2020 at 8:51
Add a ment  | 

2 Answers 2

Reset to default 2

Upon the answer you linked, you can return different values for each call of a mock function:

let myMock = jest.fn();

myMock
  .mockReturnValueOnce(10)
  .mockReturnValueOnce('x')
  .mockReturnValue(true);

In my opinion this is still brittle. You may modify the ponent and add another state later, and you would get confusing results.

Another way to test a React ponent is to test it like a user would by clicking things and setting values on inputs. This would fire the event handlers of the ponent, and React would update the state just as in real configuration. You may not be able to do shallow rendering though, or you may need to mock the child ponents.

If you prefer shallow rendering, maybe read initial state values from props like this:

function FooComponent({initialStateValue}) {
  const [state, setState] = useState(initialStateValue ?? []);
}

If you don't really need to test state but the effects state has on children, you should (as some ments mention) just then test the side effect and treat the state as an opaque implementation detail. This will work if your are not using shallow rendering or not doing some kind of async initialization like fetching data in an effect or something otherwise it could require more work.

If you cant do the above, you might consider removing state pletely out of the ponent and make it pletely functional. That way any state you need, you can just inject it into the ponent. You could wrap all of the hooks into a 'controller' type hook that encapsulates the behavior of a ponent or domain. Here is an example of how you might do that with a <Todos />. This code is 0% tested, its just written to show a concept.

const useTodos = (state = {}) => {
  const [todos, setTodos] = useState(state.todos);
  const id = useRef(Date.now());
  
  const addTodo = useCallback((task) => {
    setTodos((current) => [...current, { id: id.current++, pleted: false, task }]);
  }, []);

  const removeTodo = useCallback((id) => {
    setTodos((current) => current.filter((t) => t.id !== id));
  }, []);
  
  const pleteTodo = useCallback((id) => {
    setTodos((current) => current.map((t) => {
      let next = t;
      
        if (t.id === id) {
        next = { ...t, pleted: true };
      }
      
      return next;
    }))
  }, []);

  return { todos, addTodo, removeTodo, pleteTodo };
};


const Todos = (props) => {
  const { todos, onAdd, onRemove, onComplete } = props;
  
  const onSubmit = (e) => {
    e.preventDefault();
    onAdd({ task: e.currentTarget.elements['todo'].value });
  }
  
  return (
    <div classname="todos">
      <ul className="todos-list">
        {todos.map((todo) => (
          <Todo key={todo.id} onRemove={onRemove} onComplete={onComplete} />
        )}
      </ul>
      <form className="todos-form" onSubmit={onSubmit}>
        <input name="todo" />
        <button>Add</button>
      </form>
    </div>
  );
};

So now the parent ponent injects <Todos /> with todos and callbacks. This is useful for testing, SSR, ect. Your ponent can just take a list of todos and some handlers, and more importantly you can trivially test both. For <Todos /> you can pass mocks and known state and for useTodos you would just call the methods and make sure the state reflects what is expected.

You might be thinking This moves the problem up a level and it does, but you can now test the ponent/logic and increase test coverage. The glue layer would require minimal if any testing around this ponent, unless you really wanted to make sure props are passed into the ponent.

发布评论

评论列表(0)

  1. 暂无评论