简体   繁体   English

无法触发 function 使用 Jest 和 Enzyme 进行测试

[英]Unable to trigger function for testing using Jest and Enzyme

I am new to testing React, and I am attempting to test that HotelSelect.handleInputChange is called when a user changes the input value on the AsyncSelect component (connected by it's onInputChange callback).我是测试 React 的新手,我正在尝试测试当用户更改 AsyncSelect 组件上的输入值(通过它的 onInputChange 回调连接)时调用 HotelSelect.handleInputChange。

HotelSelect.js HotelSelect.js

import React, { useEffect, useState } from 'react';
import HotelSearchApi from '../api/HotelSearchApi';
import AsyncSelect from 'react-select/async';
import './HotelSelect.css';

export default function HotelSelect({
  ...props
}) {
  const [searchTerm, setSearchTerm] = useState('');
  const [selectValue, setSelectValue] = useState({value: '', label: ''});

  useEffect(() => {
    setSelectValue({value: props.hotel.brand_code, label: props.hotel.brand_code});
  }, [props.hotel])

  function formatHotelList(hotels) {
    return hotels.map(function(hotel) {
      return {...hotel, ...{ value: hotel.brand_code, label: hotel.brand_code } }
    });
  }

  function handleInputChange(newTerm) {
    setSearchTerm(newTerm);
  }

  async function loadOptions() {
    let hotels =  await HotelSearchApi.search(searchTerm);
    return formatHotelList(hotels);
  }

  return (
    <AsyncSelect cacheOptions={true}
                 className="HotelSelect"
                 defaultInputValue={props.hotel.brand_code}
                 value={selectValue}
                 loadOptions={loadOptions}
                 onChange={props.changeHotel}
                 onInputChange={handleInputChange}/>
  )
};

HotelSelect.test.js HotelSelect.test.js

import React from 'react';
import HotelSelect from './HotelSelect';
import AsyncSelect from 'react-select/async';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';

const hotel = {
  brand_code: '12345'
}

const searchData = {
  data: [
    {
      id: '123',
      type: 'hotel',
      attributes: {
        brand_code: '55555',
        name: 'A Hotel'
      }
    }
  ]
}

it('renders correctly', () => {
  const selector = renderer
    .create(<HotelSelect hotel={hotel} />)
    .toJSON();
  expect(selector).toMatchSnapshot();
});

it('should update searchTerm as user changes input', () => {
  const myF = jest.fn();
  const selector = mount(<HotelSelect hotel={hotel} />);
  selector.handleInputChange = myF;

  let input = selector.find('input');
  input.simulate('change', { target: { value: '5' } });

  expect(myF).toHaveBeenCalled();
});

When I use console.log to inspect selector it looks like the mock function is attached:当我使用 console.log 检查选择器时,它看起来像附加了模拟 function:

  ● Console

    console.log src/components/HotelSelect.test.js:38
      ReactWrapper {
        handleInputChange: [Function: mockConstructor] {
          _isMockFunction: true,
          getMockImplementation: [Function],
          mock: [Getter/Setter],
          mockClear: [Function],
          mockReset: [Function],
          mockRestore: [Function],
          mockReturnValueOnce: [Function],
          mockResolvedValueOnce: [Function],
          mockRejectedValueOnce: [Function],
          mockReturnValue: [Function],
          mockResolvedValue: [Function],
          mockRejectedValue: [Function],
          mockImplementationOnce: [Function],
          mockImplementation: [Function],
          mockReturnThis: [Function],
          mockName: [Function],
          getMockName: [Function]
        }
      }

Unfortunately the function doesn't seem to get triggered though:不幸的是,function 似乎没有被触发:

  ● should update searchTerm as user changes input

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

      37 |   input.simulate('change', { target: { value: '5' } });
      38 |   console.log(selector);
    > 39 |   expect(myF).toHaveBeenCalled();
         |               ^
      40 | });
      41 | 

      at Object.<anonymous> (src/components/HotelSelect.test.js:39:15)

I must not be triggering the onChange event on the input box, which should trigger the onInputChange callback on the AsyncSelect component.我不能在输入框上触发 onChange 事件,这应该触发 AsyncSelect 组件上的 onInputChange 回调。

I have read over a couple TDD postings online, the jest documentation, and at least 3 questions on Stackoverflow but I am still not able to figure this out.我已经在网上阅读了几篇 TDD 帖子、开玩笑的文档和至少 3 个关于 Stackoverflow 的问题,但我仍然无法弄清楚这一点。

Appreciate any help!感谢任何帮助!

FirstQuestion:)第一个问题:)

UPDATE: I changed the test to the following:更新:我将测试更改为以下内容:

import React from 'react';
import HotelSearchApi from '../api/HotelSearchApi';
import HotelSelect from './HotelSelect';
import AsyncSelect from 'react-select/async';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';

const hotel = {
  brand_code: '12345'
}

const searchData = {
  data: [
    {
      id: '123',
      type: 'hotel',
      attributes: {
        brand_code: '55555',
        name: 'A Hotel'
      }
    }
  ]
}

jest.mock('../api/HotelSearchApi');

it('renders correctly', () => {
  const selector = renderer
    .create(<HotelSelect hotel={hotel} />)
    .toJSON();
  expect(selector).toMatchSnapshot();
});

it('should update searchTerm as user changes input', () => {
  const selector = mount(<HotelSelect hotel={hotel} />);

  let input = selector.find('input');
  expect(HotelSearchApi.search).not.toHaveBeenCalled();
  input.simulate('change', { target: { value: '5' } });

  expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
});

But the change event doesn't seem to be updating the value in the input.但是更改事件似乎并没有更新输入中的值。 The failing test message says it is receiving '12345' which is the initial value (hotel.brand_code):失败的测试消息说它正在接收“12345”,这是初始值(hotel.brand_code):

  ● should update searchTerm as user changes input

    expect(jest.fn()).toHaveBeenCalledWith(...expected)

    Expected: "5"
    Received: "12345"

    Number of calls: 1

      39 |   input.simulate('change', { target: { value: '5' } });
      40 | 
    > 41 |   expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
         |                                 ^
      42 | });
      43 | 

      at Object.<anonymous> (src/components/HotelSelect.test.js:41:33)

The issue with with与问题

  selector.handleInputChange = myF;

Actually you cannot mock some internal variable this way.实际上,您不能以这种方式模拟某些内部变量。 If that was a method in class-based component you could do that as如果这是基于类的组件中的一种方法,您可以这样做

HotelSelect.instance().handleInputChange = jest.fn();

but it'd be a bad move anyway.但无论如何,这将是一个糟糕的举动。 You know what?你知道吗? You actually don't need to mock internal method.您实际上不需要模拟内部方法。

There is no value in checking what internal method has been called.检查调用了什么内部方法没有任何价值。 You only need to ensure some external API has been finally called and with expected parameters:您只需要确保最终调用了一些外部 API 并使用预期的参数:

import HotelSearchApi from '../api/HotelSearchApi';

jest.mock('../api/HotelSearchApi'); // automock

...
it('should update searchTerm as user changes input', () => {
  const selector = mount(<HotelSelect hotel={hotel} />);

  let input = selector.find('input');
  expect(HotelSearchApi.search).not.toHaveBeenCalled();
  input.simulate('change', { target: { value: '5' } });

  expect(HotelSearchApi.search).toHaveBeenCalledWith('5');
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM