在React Test Render中调用prop回调不会更新钩子

时间:2019-03-26 19:55:48

标签: javascript reactjs typescript jestjs

我试图弄清楚如何测试使用钩子来跟踪状态的React函数组件。我遇到的问题是,当我调用传递到输入组件的onChange prop函数时,useState钩子的值没有被更新,因此当我触发表单提交时,结果不能反映状态变化。

这是我正在测试的组件:

import React, { useState, ChangeEvent, FormEvent } from 'react';
import styled from '../styled';
import Column from './Column';
import BlockButton from './BlockButton';
import Input from './Input';
import Form from './Form';
import InputRow from './InputRow';

export type LoginFormOnSubmit = (result: LoginFormResult) => any;
export interface LoginFormResult {
  employeeId: string;
  password: string;
}
export interface LoginFormProps {
  onSubmit?: LoginFormOnSubmit;
}

const LoginForm: React.FunctionComponent<LoginFormProps> = props => {
  const { onSubmit, ...rest } = props;
  const [employeeId, setEmployeeId] = useState('');
  const [password, setPassword] = useState('');

  function handleEmployeeIdChange(event: ChangeEvent<HTMLInputElement>) {
    setEmployeeId(event.currentTarget.value);
  }

  function handlePasswordChange(event: ChangeEvent<HTMLInputElement>) {
    setPassword(event.currentTarget.value);
  }

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    if(onSubmit) {
      onSubmit({
        employeeId,
        password,
      });
    }
  }

  return (
    <Column {...rest}>
      <h1>SHIFT LEDGER LOGON</h1>
      <Form onSubmit={handleSubmit}>
        <InputRow>
          <label>Employee ID:</label>
          <Input
            type="text"
            value={employeeId}
            onChange={handleEmployeeIdChange}
            data-testid="employee-id-input"
          />
        </InputRow>
        <InputRow>
          <label>Password:</label>
          <Input
            type="password"
            value={password}
            onChange={handlePasswordChange}
            data-test-id="password-input"
          />
        </InputRow>
        <BlockButton data-testid="submit-button">Enter</BlockButton>
      </Form>
    </Column>
  );
};

export default styled(LoginForm)`
  background-color: #F0EBDF;
  justify-content: center;
  flex: 1;

  h1 {
    text-align: center;
  }

  ${Form} {
    align-items: center;
  }

  label {
    width: 150px;
  }
`;

这是我的测试:

import React from 'react';
import sinon from 'sinon';
import TestRenderer, { act } from 'react-test-renderer';
import LoginForm from '../LoginForm';
import Form from '../Form';

describe('components/LoginForm', () => {
  test('Should be able to edit employee id', async () => {
    const onSubmit = sinon.fake();
    const employeeId = 'test id';
    const rendered = TestRenderer.create(<LoginForm onSubmit={onSubmit}/>);


    act(() => {
      const wrapper = rendered.root;
      const employeeIdInput = wrapper.findByProps({ 'data-testid': 'employee-id-input' });

      employeeIdInput.props!.onChange({
        currentTarget: {
          value: employeeId,
        },
      });

      wrapper.findByType(Form).props.onSubmit({
        preventDefault: sinon.fake(),
      });
    });

    expect(onSubmit.calledOnce).toBeTruthy();
    expect(onSubmit.args).toEqual([[
      {
        employeeId,
        password: '',
      }
    ]]);
  });
});

还有我看到的错误:

  ● components/LoginForm › Should be able to edit employee id

    expect(received).toEqual(expected)

    Difference:

    - Expected
    + Received

      Array [
        Array [
          Object {
    -       "employeeId": "test id",
    +       "employeeId": "",
            "password": "",
          },
        ],
      ]

      42 |     });
      43 |
    > 44 |     expect(onSubmit.calledOnce).toBeTruthy();
         |                                 ^
      45 |     expect(onSubmit.args).toEqual([[
      46 |       {
      47 |         employeeId,

      at react_test_renderer_1.act (src/components/__tests__/LoginForm.test.tsx:44:33)
      at batchedUpdates (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12492:12)
      at Object.act (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13146:18)
      at Object.test (src/components/__tests__/LoginForm.test.tsx:29:5)

一些额外的信息:

  • 打字稿3.3.3
  • 反应16.8.2
  • react-dom 16.8.2
  • react-test-renderer 16.8.5
  • 开玩笑24.3.1

1 个答案:

答案 0 :(得分:1)

问题似乎在于,状态{em}在act函数期间不更新,它仅在act完成后更新

这是一个简单的例子:

import React, { useState } from 'react';
import TestRenderer, { act } from 'react-test-renderer';

function Example(props) {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)} data-test-id="increment">
        Increment
      </button>
      <button onClick={() => props.submit(count)} data-test-id="submit">
        Submit
      </button>
    </div>
  );
}

test('Example', () => {
  const submitSpy = jest.fn();
  const rendered = TestRenderer.create(<Example submit={submitSpy} />);

  act(() => {
    rendered.root.findByProps({ 'data-test-id': 'increment' }).props.onClick();  // increment
    rendered.root.findByProps({ 'data-test-id': 'submit' }).props.onClick();  // submit
    expect(rendered.root.findByType('p').props.children).toBe(0);  //  <= still 0
    expect(submitSpy).toHaveBeenLastCalledWith(0);  //  <= still 0
  });

  expect(rendered.root.findByType('p').props.children).toBe(1);  //  <= NOW it's 1
  rendered.root.findByProps({ 'data-test-id': 'submit' }).props.onClick();  // submit again
  expect(submitSpy).toHaveBeenLastCalledWith(1);  // Success!
});

因此,看起来像在act之后 之后提交表单,而不是在act 期间提交