单元测试不会更新样式化输入的值

Unit test doesn't update value on styled input

我正在测试 React 组件

import React from 'react';

import styled from 'styled-components';

const Input = styled.input``;

export default function FormInput (props) {
  const { name, type, value, label, placeholder, onChange} = props;

  return (
      <Input
        id={name}
        name={name}
        type={type}
        placeholder={placeholder}
        onChange={onChange}
        value={value}
      />
  )
};

使用这个单元测试

test('it changes the value of the input', () => {
  let currentValue = 'Initial value';
  const setValue = (value) => {
    currentValue = value;
  }

  const component = <FormInput 
    name={'firstName'} 
    placeholder={'Enter your first name'} 
    value={currentValue} 
    onChange={(e) => setValue(e.target.value)} />

  render(component);

  const input = screen.getByPlaceholderText('Enter your first name');

  fireEvent.change(input, {target: {value: 'New value'}});

  expect(currentValue).toBe('New value');
  expect(input.value).toBe('New value');
});

关于输入值的断言是假的,因为它仍然是初始值。该组件在应用程序中正常工作。我在测试中设置错了什么?

我发现使用 @testing-library/user-event 包很容易模拟用户的文本输入。

import userEvent from '@testing-library/user-event';

test('it changes the value of the input', () => {
  let currentValue = 'Initial value';
  const setValue = (value) => {
    currentValue = value;
  }

  const component = <FormInput 
    name={'firstName'} 
    placeholder={'Enter your first name'} 
    value={currentValue} 
    onChange={(e) => setValue(e.target.value)} />

  render(component);

  const input = screen.getByPlaceholderText('Enter your first name');

  userEvent.type(input, 'New value');

  expect(currentValue).toBe('New value');
  expect(input.value).toBe('New value');
});

为什么不将 setValue 放在组件主体中?这个逻辑明明是属于组件的,你应该有:

import React, { useState } from 'react';

import styled from 'styled-components';

const Input = styled.input``;

export default function FormInput (props) {
  const { name, type, label, placeholder} = props;
  const [value, setValue] = useState('');

  return (
      <input
        id={name}
        name={name}
        type={type}
        placeholder={placeholder}
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
  )
};

然后在你的测试中:

import '@testing-library/jest-dom/extend-expect';

test('it changes the value of the input', () => {
    const component = <FormInput 
        name={'firstName'} 
        placeholder={'Enter your first name'} 
        value={currentValue} />

    render(component);

    const input = screen.getByPlaceholderText('Enter your first name');

    fireEvent.change(input, { target : { value : 'New value' } });
    expect(input).toHaveValue('New value');
});

请注意,我使用 https://github.com/testing-library/jest-dom 以获得更具可读性的匹配器。

单元测试失败,因为它没有被重新渲染。我使用 rerender 函数来渲染具有更新值的组件。这是必要的,因为此测试不包括父组件保持状态。

更新测试

test('it changes the value of the input', async () => {
  let currentValue = 'Initial value';
  const setValue = (value) => {
    currentValue = value;
  }

  const component = <FormInput 
    name={'firstName'} 
    placeholder={'Enter your first name'} 
    value={currentValue} 
    onChange={(e) => setValue(e.target.value)} />

  const { rerender } = render(component);

  const input = screen.getByPlaceholderText('Enter your first name');

  fireEvent.change(input, {target: {value: 'New value'}});

  rerender(<FormInput value={currentValue} />);

  expect(input.value).toBe('New value');
});