programing

React Select 컴포넌트 테스트

subpage 2023. 2. 23. 22:47
반응형

React Select 컴포넌트 테스트

https://github.com/JedWatson/react-select

리액트 셀렉트 리액트 컴포넌트를 사용하고 싶은데 테스트를 추가해야 합니다.

구글에서 찾은 몇 가지 옵션을 시도해 봤지만 효과가 없는 것 같습니다.아래의 코드를 가지고 있습니다만, 변경 이벤트가 발생하고 있는 것은 아닙니다.'is-focused' 클래스를 추가하는 포커스 이벤트를 추가할 수 있었지만 'is-open' 클래스가 아직 없습니다.

https://github.com/JedWatson/react-select/blob/master/test/Select-test.js 를 참조로 사용하고 있습니다.

입력 필드에서만 변경 이벤트를 사용하려고 했지만, 이것도 도움이 되지 않았습니다. '이렇게 하다'라는 게 있더라고요.onInputChange={this.change}를 선택합니다.

시험

import Home from '../../src/components/home';
import { mount } from 'enzyme'

describe('Home', () => {

it("renders home", () => {

    const component = mount(<Home/>);

    // default class on .Select div
    // "Select foobar Select--single is-searchable"

    const select = component.find('.Select');

    // After focus event
    // "Select foobar Select--single is-searchable is-focussed"
    // missing is-open
    TestUtils.Simulate.focus(select.find('input'));

    //this is not working
    TestUtils.Simulate.keyDown(select.find('.Select-control'), { keyCode: 40, key: 'ArrowDown' });
    TestUtils.Simulate.keyDown(select.find('.Select-control'), { keyCode: 13, key: 'Enter' });

    // as per code below I expect the h2 to have the select value in it eg 'feaure'

});
});

테스트 대상 컴포넌트

import React, { Component } from 'react';
import Select from 'react-select';

class Home extends Component {
constructor(props) {
    super(props);

    this.state = {
        message: "Please select option"};
    this.change = this.change.bind(this);
}

change(event) {

    if(event.value) {
        this.setState({message: event.label});
    }
}

render () {

    const options = [ {label: 'bug', value: 1} , {label: 'feature', value: 2 }, {label: 'documents', value: 3}, {label: 'discussion', value: 4}];

    return (
      <div className='content xs-full-height'>
          <div>
              <h2>{this.state.message}</h2>

              <Select
                  name="select"
                  value={this.state.message}
                  options={options}
                  onInputChange={this.change}
                  onChange={this.change}
              />

          </div>
        </div>
    );
}
}

export default Home;

명령줄 테스트를 실행하려면 다음 작업을 수행합니다.

>> npm run test

package.js에는 다음 스크립트가 있습니다.

"test": "mocha --compilers js:babel-core/register -w test/browser.js ./new",

테스트 셋업

및 browser.filename은 다음과 같습니다.

import 'babel-register';
import jsdom from 'jsdom';

const exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
   if (typeof global[property] === 'undefined') {
       exposedProperties.push(property);
       global[property] = document.defaultView[property];
   }
});

global.navigator = {
    userAgent: 'node.js'
};

또, https://github.com/StephenGrider/ReduxSimpleStarter 에 기재되어 있는 테스트 방법을 사용해 보았습니다.

어떤 도움이라도 감사히 받겠습니다

이것은 반복적인 질문입니다.소스 코드의 100%를 커버하는 100% 합격 테스트로 나만의 코드를 공유하고 있습니다.

내 컴포넌트는 이렇게 생겼다.

MySelectComponent({ options, onChange }) {

  return <div data-testid="my-select-component">
    <Select
      className="basic-single"
      classNamePrefix="select"
      name="myOptions"
      placeholder="Select an option"
      options={options}
      onChange={e => onChange(e)}
    />
</div>;
}

Selectdata-testid="my-select-component"렌더링된 옵션 요소를 사용할 수 있게 됩니다.그렇지 않으면 텍스트 옵션이 있는지 확인할 수 없습니다(테스트를 보면 더 잘 알 수 있습니다).

이것은 라이브 실행 예이며 렌더링 시 10가지 옵션이 있는 선택 컴포넌트가 표시됩니다.

여기에 이미지 설명 입력

테스트 1 : 오류 없이 렌더링해야 합니다.

  • 구성 요소를 렌더링합니다.

  • 자리 표시자를 검색합니다.

테스트 2 : 첫 번째 옵션이 선택되었을 때 onChange를 호출해야 합니다.

  • 구성 요소를 렌더링합니다.

  • 는 내 것이 확인합니다.mockedOnChange아직 호출되지 않았습니다.

  • 「」의 시뮬레이션을 합니다.ArrowDown

  • 첫 번째 옵션을 클릭합니다.

  • mockedOnChange첫 번째 옵션라벨과 값을 사용하여1 time 이라고 불립니다.

테스트 3 : 첫 번째 옵션을 선택한 후 두 번째 옵션을 선택한 후 아홉 번째 옵션을 선택한 경우 onChange를 호출해야 합니다.

  • 구성 요소를 렌더링합니다.

  • 첫 번째 옵션 중 하나를 시뮬레이트합니다.

  • 두 번째 옵션 중 하나를 시뮬레이션합니다.

  • 9번째 옵션 중 하나를 시뮬레이션합니다.

  • .mockedOnChange9번째 옵션 베일과 값을 사용하여 3회 호출됩니다.

테스트 4 : 입력값으로 필터링할 때 onChange를 호출해야 합니다.

  • 구성 요소를 렌더링합니다.

  • "option 1"을 입력하여 입력 필드의 변화를 시뮬레이션합니다.

  • 요, 제 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.mockedOptions「Mocked option 1」 「Mocked option 10」은 「Mocked option 10 。

  • 는 2개의 시뮬레이션을 .ArrowDown벤트입입니니다

  • mockedOnChange올바른 라벨과 값을 가진 두 번째 필터링 옵션을 사용하여 호출됩니다.

테스트 파일 완성

import React from 'react';
import { render, fireEvent, cleanup, waitForElement } from '@testing-library/react';
import MySelectComponent from './MySelectComponent';

afterEach(cleanup);

describe ('Test react-select component', () => {

    const mockedOptions = [
        {label: 'Mocked option 1', value: 'mocked-option-1'},
        {label: 'Mocked option 2', value: 'mocked-option-2'},
        {label: 'Mocked option 3', value: 'mocked-option-3'},
        {label: 'Mocked option 4', value: 'mocked-option-4'},
        {label: 'Mocked option 5', value: 'mocked-option-5'},
        {label: 'Mocked option 6', value: 'mocked-option-6'},
        {label: 'Mocked option 7', value: 'mocked-option-7'},
        {label: 'Mocked option 8', value: 'mocked-option-8'},
        {label: 'Mocked option 9', value: 'mocked-option-9'},
        {label: 'Mocked option 10', value: 'mocked-option-10'},
    ];

    it('should render without errors', async () => {
        const mockedOnChange = jest.fn();
        const { getByText } = render(<MySelectComponent 
            options={mockedOptions} 
            onChange={mockedOnChange} />);

        const placeholder = getByText('Select an option');

        expect(placeholder).toBeTruthy();
    });

    it('should call onChange when the first option is selected', async () => {
        const mockedOnChange = jest.fn();
        const { getByText, queryByTestId } = render(<MySelectComponent 
            options={mockedOptions} 
            onChange={mockedOnChange} />);

        const mySelectComponent = queryByTestId('my-select-component');

        expect(mySelectComponent).toBeDefined();
        expect(mySelectComponent).not.toBeNull();
        expect(mockedOnChange).toHaveBeenCalledTimes(0);

        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
        await waitForElement(() => getByText('Mocked option 1'));
        fireEvent.click(getByText('Mocked option 1'));

        expect(mockedOnChange).toHaveBeenCalledTimes(1);
        expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 1', value: 'mocked-option-1'});

    });

    it('should call onChange when the first option is selected then second option then the 9th one', async () => {
        const mockedOnChange = jest.fn();
        const { getByText, queryByTestId } = render(<MySelectComponent 
            options={mockedOptions} 
            onChange={mockedOnChange} />);

        const mySelectComponent = queryByTestId('my-select-component');

        expect(mySelectComponent).toBeDefined();
        expect(mySelectComponent).not.toBeNull();
        expect(mockedOnChange).toHaveBeenCalledTimes(0);

        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
        await waitForElement(() => getByText('Mocked option 1'));
        fireEvent.click(getByText('Mocked option 1'));

        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
        await waitForElement(() => getByText('Mocked option 2'));
        fireEvent.click(getByText('Mocked option 2'));

        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
        await waitForElement(() => getByText('Mocked option 9'));
        fireEvent.click(getByText('Mocked option 9'));

        expect(mockedOnChange).toHaveBeenCalledTimes(3);
        expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 9', value: 'mocked-option-9'});
    });

    it('should call onChange when filtering by input value', async () => {
      const mockedOnChange = jest.fn();
      const { getByText, queryByTestId, container } = render(<MySelectComponent 
        options={mockedOptions} 
        onChange={mockedOnChange} />);

        const mySelectComponent = queryByTestId('my-select-component');

        fireEvent.change(container.querySelector('input'), {
            target: { value: 'option 1' },
        });

        // select Mocked option 1
        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });  
        // select Mocked option 10
        fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });

        await waitForElement(() => getByText('Mocked option 10'));
        fireEvent.click(getByText('Mocked option 10'));

        expect(mockedOnChange).toHaveBeenCalledTimes(1);
        expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 10', value: 'mocked-option-10'});
    });

});

이게 도움이 됐으면 좋겠어요.

위의 두 가지 답을 모두 시도해 봤지만, 아직 성공하지 못했습니다.

나에게 효과가 있었던 것은, 다음과 같다.

  1. classNamePrefix- 즉 - 즉,list답변에서 와 같이 (다른 답변에서 설명한 바와 같이)

    <Select
       classNamePrefix='list'
       options={[
         { label: 'one', value: 'one' },
         { label: 'two', value: 'two' }
    ]}/>
    
  2. 드롭다운 표시기를 선택하고 마우스를 시뮬레이션합니다.다운 => 열린 드롭다운:

    wrapper
      .find('.list__dropdown-indicator')
      .simulate('mouseDown', {
        button: 0 
      });
    
  3. 내 경우 드롭다운 옵션 수를 확인하는 등 어떤 일이 일어날 것으로 예상합니다.

    expect(wrapper.find('.list__option').length).toEqual(2);
    

    할 수 있는 에는 '소품', '소품', '소품', '소품', '소품'을 붙일 수 .menuIsOpen항상 메뉴를 열어두도록 합니다(목록의 2단계라고도 함).

드롭다운을 연 후 드롭다운에서 값을 선택하려면:

wrapper.find('.list__option').last().simulate('click', null);

다음 중 하나를 테스트할 수 있습니다.

expect(wrapper.find('.list__value-container').text()).toEqual('two');

또는

expect(wrapper.find('.list__single-value').text()).toEqual('two');

https://github.com/JedWatson/react-select/issues/1357 에서

제가 찾은 유일한 솔루션은 키 다운 이벤트를 통해 선택을 시뮬레이션하는 것이었습니다.

wrapper.find('.Select-control').simulate('keyDown', { keyCode: 40 });
// you can use 'input' instead of '.Select-control'
wrapper.find('.Select-control').simulate('keyDown', { keyCode: 13 });
expect(size).to.eql('your first value in the list')

테스트 라이브러리 및 v2.0 사용

다음과 같은 매우 구체적인 것은 사용하지 않으려고 합니다.classNamePrefix또는 onChange 소품 등을 찾아 컴포넌트의 작동 방식을 해킹합니다.

const callback = jest.fn();
const { container, getByText} = render(<Select ... onChange={callback} />);

여기서는 기본적으로 화면 판독기를 가장하고 초점을 맞춘 다음 아래쪽 화살표를 누릅니다.

fireEvent.focus(container.querySelector('input'));
fireEvent.keyDown(container.querySelector('input'), { key: 'ArrowDown', code: 40 });

원하는 값을 클릭합니다.

fireEvent.click(getByText('Option Two'));

그리고 단언하라.

expect(callback).toHaveBeenCalledWith({ value: 'two', label: 'Option Two'});

Keith가 말한 것에 더해 시뮬레이션 방법을 사용하는 것이 기능을 발휘할 수 있는 유일한 방법인 것 같습니다.그러나 솔루션에서 이 방법을 사용해 보니 작동하지 않았습니다.Typescript를 사용하고 있기 때문에 이것이 관련이 있는지는 확실하지 않지만 이벤트를 시뮬레이트할 때는 키 속성을 전달할 필요가 있음을 알 수 있었습니다.

wrapper.find('.Select-control').simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });
wrapper.find('.Select-control').simulate('keyDown', { key: 'Enter', keyCode: 13 });

또한 classNamePrefix 속성을 설정하면 컴포넌트가 시뮬레이션된 이벤트에 올바르게 응답하고 있음을 확신하기 위해 간단한 테스트를 수행하는 것이 훨씬 쉬워졌습니다.이 접두사를 설정하면 컴포넌트의 유용한 부분이 클래스 이름으로 장식되어 쉽게 액세스할 수 있습니다(Google 개발 도구에서 요소를 검사하여 이러한 유용한 클래스 이름을 식별할 수 있습니다).간단한 Jest 테스트:

it('react-select will respond to events correctly', () => {
    const sut = Enzyme.mount(
    <Select 
        classNamePrefix="list" 
        options={[{ label: 'item 1', value: 1 }]}
    />);

    // The intereactive element uses the suffix __control **note there are two underscores** 
    sut.find('.list__control').first().simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });
    sut.find('.list__control').first().simulate('keyDown', { key: 'Enter', keyCode: 13 });

    // the selected value uses the suffix __single-value **note there are two underscores** 
    expect(sut.find('.list__single-value').first().text()).toEqual('item 1');
});

react-select(2.x+)의 새로운 버전에서는 react-select는 감정을 사용하기 때문에 위의 방법은 작동하지 않습니다.따라서,wrapper.find('.Select-control')또는wrapper.find('.list__option')더 이상 작동하지 않습니다.resact-select 2.x+는 다음 명령어를Select스테이트 매니저 내의 컴포넌트에 액세스 할 수 있습니다.onChange의 방법Select요소.다음 코드를 사용하여 선택을 트리거합니다.

wrapper.find(Select).props().onChange({ value: ... })

덧붙이고 싶은 게 있는데, 사실 가 더해야 했던 건classNamePrefix을 지탱하다.Select컴포넌트 이외에는 아무것도 얻지 못했습니다.*__control수강할 수 있습니다.

만약 누군가가 효소를 사용한다면, 이것은 나에게 효과가 있었다.

  wrapper.find('Select').simulate('change', {
    target: { name: 'select', value: 1 },
  });

여기서 "select"는 여기서 정의한 Atribute 이름입니다.

  <Select
      name="select"
      ...

"value"는 원하는 옵션 값입니다.

리액트 테스트 라이브러리 사용 시:

<Select id='myId' onChange=(list: ReactSelectOption[]) => {
                        props.changeGroupItem(
                            {
                                items: list ? list.map((item) => item.value) : [],
                            }
                        );
                    }
/>

그리고 내 시험에서는

const selectInput = container.querySelector(`#myId input`) as HTMLInputElement;
    fireEvent.focus(selectInput);
    fireEvent.mouseDown(selectInput);
    fireEvent.click(getByText("myValue"));
    expect(props.changeGroupItem).toHaveBeenCalledWith(
        {
            items: ["myDefaultValue", "myValue"],
        }
    );

react-testing-library와 함께 사용하기 위해 react-select 요소에서 사용자 이벤트를 시뮬레이션하는 라이브러리가 있습니다.반응 선택 2+와 함께 작동합니다.

https://www.npmjs.com/package/react-select-event

다음과 같은 경우:

const { getByRole, getByLabelText } = render(
  <form role="form">
    <label htmlFor="food">Food</label>
    <Select options={OPTIONS} name="food" inputId="food" isMulti />
  </form>
);
expect(getByRole("form")).toHaveFormValues({ food: "" });
 
await selectEvent.select(getByLabelText("Food"), ["Strawberry", "Mango"]);
expect(getByRole("form")).toHaveFormValues({ food: ["strawberry", "mango"] });
 
await selectEvent.select(getByLabelText("Food"), "Chocolate");
expect(getByRole("form")).toHaveFormValues({
  food: ["strawberry", "mango", "chocolate"],
});

효소 v3.11.0을 사용하고 react-select v3.0.8을 사용하는 분들에게 이것은 저에게 효과가 있었습니다.

component.find('Select').simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });

여기 제 것이 있습니다.Select와 함께 사용하고 있습니다.redux-form

<Select
    {...input}
    styles={customStyles}
    options={props.options}
    formatGroupLabel={formatGroupLabel}
    placeholder="Custom Search..."
    isSearchable={true}
    onBlur={handleBlur}
/>

테스트의 샘플

it('should render dropdown on keyDown', () => {
    expect(component.find('MenuList').length).toEqual(1);
});

it('should render the correct amount of options', () => {
    expect(component.find('Option').length).toEqual(optionsLength);
});
wrapper.find(ReactSelectComponent.instance().onChange(...params));

여기서 모든 해결책을 시도해 봤지만 아무 것도 효과가 없었습니다.

사용자 이벤트 라이브러리로 문제를 해결할 수 있었습니다.체크 아웃selectOptions기능.

리액트 셀렉트 테스트에서도 같은 문제가 발생했기 때문에 해결 방법은 다음과 같습니다.

리액트 셀렉트:

      <Select
      options={options}
      placeholder="Select an Option"
    />

테스트:

  it('should select an option', () => {
    const { getByText } = render(
      <MySelect/>
    );

    fireEvent.focus(getByText('Select an Option'));

    fireEvent.keyDown(getByText('Select an Option'), {
      key: 'ArrowDown',
      code: 40,
    });

    fireEvent.click(getByText("my wanted option"));
    expect(getByText("my wanted option")).toBeDefined();
  }

https://stackoverflow.com/a/57699061/10786373은 거의 나에게 효과가 있다.선택 메뉴를 열기 위한 keyDown 이벤트를 추가했습니다.

it('my test', () => {
  const { container } = getShallow();
  const elSelector = container.querySelector('.Select-input');

  expect(propsComponent.onPageSizeChange).toHaveBeenCalledTimes(0);

  // open select menu
  fireEvent.keyDown(elSelector, { keyCode: 13 });

  // choose next option
  fireEvent.keyDown(elSelector, { key: 'ArrowDown', code: 40 });

  // send the option
  fireEvent.keyDown(elSelector, { keyCode: 13 });

  expect(propsComponent.onPageSizeChange).toHaveBeenCalledTimes(1);
});

언급URL : https://stackoverflow.com/questions/41991077/testing-react-select-component

반응형