리액트 테스트 라이브러리를 사용하여 재료 UI 자동 완성 테스트 방법
material-ui autocomplete 컴포넌트를 사용하고 있으며 react-testing-library를 사용하여 테스트하려고 합니다.
컴포넌트:
/* eslint-disable no-use-before-define */
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import React from 'react';
export default function ComboBox() {
const [autocompleteInputValue, setAutocompleteInputValue] = React.useState('');
const [isAutocompleteOpen, setIsAutocompleteOpen] = React.useState(false);
const renderInput = (params: any) => <TextField {...params} label='openOnFocus: false' variant='outlined' />;
const getTitle = (option: any) => option.title;
const handleAutocompleteInputChange = (event: any, value: string) => {
setAutocompleteInputValue(value);
};
const updateAutocompletePopper = () => {
setIsAutocompleteOpen(!isAutocompleteOpen);
};
return (
<Autocomplete
id='autocompleteSearch'
data-testid='autocomplete-search'
disableClearable={true}
renderOption={getTitle}
getOptionLabel={getTitle}
renderInput={renderInput}
options={top100Films}
clearOnEscape={true}
onInputChange={handleAutocompleteInputChange}
inputValue={autocompleteInputValue}
open={isAutocompleteOpen}
onOpen={updateAutocompletePopper}
onClose={updateAutocompletePopper}
style={{ width: 300 }}
ListboxProps={{ 'data-testid': 'list-box' }}
/>
);
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
export const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: 'Schindlers List', year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{ title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
{ title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{ title: 'The Lord of the Rings: The Two Towers', year: 2002 },
{ title: 'One Flew Over the Cuckoos Nest', year: 1975 },
{ title: 'Goodfellas', year: 1990 },
{ title: 'The Matrix', year: 1999 },
{ title: 'Seven Samurai', year: 1954 },
{ title: 'Star Wars: Episode IV - A New Hope', year: 1977 },
{ title: 'City of God', year: 2002 },
{ title: 'Se7en', year: 1995 },
{ title: 'The Silence of the Lambs', year: 1991 },
{ title: 'Its a Wonderful Life', year: 1946 },
{ title: 'Life Is Beautiful', year: 1997 },
{ title: 'The Usual Suspects', year: 1995 },
{ title: 'Léon: The Professional', year: 1994 },
{ title: 'Spirited Away', year: 2001 },
{ title: 'Saving Private Ryan', year: 1998 },
{ title: 'Once Upon a Time in the West', year: 1968 },
{ title: 'American History X', year: 1998 },
{ title: 'Interstellar', year: 2014 },
{ title: 'Casablanca', year: 1942 },
{ title: 'City Lights', year: 1931 },
{ title: 'Psycho', year: 1960 },
{ title: 'The Green Mile', year: 1999 },
{ title: 'The Intouchables', year: 2011 },
{ title: 'Modern Times', year: 1936 },
{ title: 'Raiders of the Lost Ark', year: 1981 },
{ title: 'Rear Window', year: 1954 },
{ title: 'The Pianist', year: 2002 },
{ title: 'The Departed', year: 2006 },
{ title: 'Terminator 2: Judgment Day', year: 1991 },
{ title: 'Back to the Future', year: 1985 },
{ title: 'Whiplash', year: 2014 },
{ title: 'Gladiator', year: 2000 },
{ title: 'Memento', year: 2000 },
{ title: 'The Prestige', year: 2006 },
{ title: 'The Lion King', year: 1994 },
{ title: 'Apocalypse Now', year: 1979 },
{ title: 'Alien', year: 1979 },
{ title: 'Sunset Boulevard', year: 1950 },
{
title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb',
year: 1964,
},
{ title: 'The Great Dictator', year: 1940 },
{ title: 'Cinema Paradiso', year: 1988 },
{ title: 'The Lives of Others', year: 2006 },
{ title: 'Grave of the Fireflies', year: 1988 },
{ title: 'Paths of Glory', year: 1957 },
{ title: 'Django Unchained', year: 2012 },
{ title: 'The Shining', year: 1980 },
{ title: 'WALL·E', year: 2008 },
{ title: 'American Beauty', year: 1999 },
{ title: 'The Dark Knight Rises', year: 2012 },
{ title: 'Princess Mononoke', year: 1997 },
{ title: 'Aliens', year: 1986 },
{ title: 'Oldboy', year: 2003 },
{ title: 'Once Upon a Time in America', year: 1984 },
{ title: 'Witness for the Prosecution', year: 1957 },
{ title: 'Das Boot', year: 1981 },
{ title: 'Citizen Kane', year: 1941 },
{ title: 'North by Northwest', year: 1959 },
{ title: 'Vertigo', year: 1958 },
{ title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 },
{ title: 'Reservoir Dogs', year: 1992 },
{ title: 'Braveheart', year: 1995 },
{ title: 'M', year: 1931 },
{ title: 'Requiem for a Dream', year: 2000 },
{ title: 'Amélie', year: 2001 },
{ title: 'A Clockwork Orange', year: 1971 },
{ title: 'Like Stars on Earth', year: 2007 },
{ title: 'Taxi Driver', year: 1976 },
{ title: 'Lawrence of Arabia', year: 1962 },
{ title: 'Double Indemnity', year: 1944 },
{ title: 'Eternal Sunshine of the Spotless Mind', year: 2004 },
{ title: 'Amadeus', year: 1984 },
{ title: 'To Kill a Mockingbird', year: 1962 },
{ title: 'Toy Story 3', year: 2010 },
{ title: 'Logan', year: 2017 },
{ title: 'Full Metal Jacket', year: 1987 },
{ title: 'Dangal', year: 2016 },
{ title: 'The Sting', year: 1973 },
{ title: '2001: A Space Odyssey', year: 1968 },
{ title: 'Singin in the Rain', year: 1952 },
{ title: 'Toy Story', year: 1995 },
{ title: 'Bicycle Thieves', year: 1948 },
{ title: 'The Kid', year: 1921 },
{ title: 'Inglourious Basterds', year: 2009 },
{ title: 'Snatch', year: 2000 },
{ title: '3 Idiots', year: 2009 },
{ title: 'Monty Python and the Holy Grail', year: 1975 },
];
오토 컴플리트 중에서 선택한 옵션에 따라 칩 렌더링이나 다른 컴포넌트 등 다른 작업을 하고 있습니다.간단히 말하면 처음에 테스트하는 것은 사용자가 입력 필드에 포커스를 맞추면 팝업이 표시되므로 나중에 이 팝업에서 옵션을 클릭하여 다른 모든 것이 예상대로 작동하는지 테스트할 수 있습니다.가 지금 .data-testid
는 록록 through through through through through through through through through through 를 통해서 목록란에 배정을 .ListboxProps
동동: :
테스트:
import {
fireEvent,
getByRole as globalGetByRole,
getByText as globalGetByText,
render,
} from '@testing-library/react';
import React from 'react';
import ComboBox, { top100Films } from './AutoComplete';
test('that autocomplete works', async () => {
const { getByTestId, getByRole, queryByRole } = render(<ComboBox />, {});
const AutoCompleteSearch = getByTestId('autocomplete-search');
const Input = globalGetByRole(AutoCompleteSearch, 'textbox');
expect(queryByRole('listbox')).toBeNull();
fireEvent.mouseDown(Input);
const ListBox = getByRole('listbox');
expect(ListBox).toBeDefined();
const menuItem1 = globalGetByText(ListBox, top100Films[0].title);
fireEvent.click(menuItem1);
expect(queryByRole('listbox')).toBeNull();
fireEvent.mouseDown(Input);
const ListBoxAfter = getByRole('listbox');
expect(ListBoxAfter).toBeDefined();
const menuItem2 = globalGetByText(ListBoxAfter, top100Films[1].title);
fireEvent.click(menuItem2);
expect(queryByRole('listbox')).toBeNull();
});
그러나 이 문제는 다음과 같습니다.Unable to find an element by: [data-testid="list-box"]
가가 뭘못 ?? ???
편집: 해고했습니다.mouseDown
Input
팝업이 열리는지 테스트할 수 있었습니다.하였습니다.listbox
대신 역할 역할 대신 역할.data-testid
이치노 해서 할 수 요.data-testid
자동 이 닫힙니다.그런 다음 자동 완성 옵션에서 항목을 선택하고 팝업을 닫았습니다.두 번째로 팝업을 다시 열려고 했는데 여기서 또 실패합니다.하여 를 두 열 수.mouseDown
먼저 옵션이 빈 배열이 아닌지 확인한 후 다음을 수행해야 합니다.
const autocomplete = getByTestId('autocomplete');
const input = within(autocomplete).getByRole('textbox')
autocomplete.focus()
// the value here can be any string you want, so you may also consider to
// wrapper it as a function and pass in inputValue as parameter
fireEvent.change(input, { target: { value: 'a' } })
fireEvent.keyDown(autocomplete, { key: 'ArrowDown' })
fireEvent.keyDown(autocomplete, { key: 'Enter' })
목록 항목은 DOM 자체에서 "표시"되지 않으므로 다른 방법을 사용해야 합니다.
이벤트를 트리거할 자동 완성 및 입력 DOM 요소를 찾아야 합니다.
자동완성은 보통 다음과 같은 역할 속성에 의해 DOM에서 찾을 수 있습니다. role="combobox"
가장 .data-testid="autocomplete"
다음 코드는 항목 선택을 자동 완료로 테스트하는 방법을 보여 줍니다.
const autocomplete = getByTestId('autocomplete');
const input = within(autocomplete).querySelector('input')
autocomplete.focus()
// assign value to input field
fireEvent.change(input, { target: { value: value } })
await wait()
// navigate to the first item in the autocomplete box
fireEvent.keyDown(autocomplete, { key: 'ArrowDown' })
await wait()
// select the first item
fireEvent.keyDown(autocomplete, { key: 'Enter' })
await wait()
// check the new value of the input field
expect(input).toHaveValue('some_value')
입력 요소에 값을 삽입한 후 변경을 트리거해야 합니다.그런 다음 목록 상자가 열리고 Enter 키를 눌러 첫 번째 값을 선택할 수 있습니다.선택한 값은 자동 완성 검색/열기에 사용되는 입력된 초기 값을 대체합니다.
// make sure autocomplete reactions/results do not already exist
expect(screen.queryByText(/Loading/)).not.toBeInTheDocument()
expect(screen.queryByText(/Van Halen/)).not.toBeInTheDocument()
// fill out autocomplete
const faveBand = screen.getByLabelText(/Favorite Band/)
userEvent.type(faveBand, 'Van H')
expect(faveBand).toHaveValue('Van H')
// witness autocomplete working
expect(screen.getByText(/Loading/)).toBeInTheDocument()
// wait for response (i used an async Material-UI autocomplete)
// favebands is a data-testid attribute value in my autocomplete
// component, e.g. ListboxProps={{ 'data-testid': 'favebands' }}
await waitFor(() => getByTestId('favebands'))
// verify autocomplete items are visible
expect(screen.getByText(/Van Halen/)).toBeInTheDocument()
// click on autocomplete item
const faveBandItem = screen.getByText('Van Halen')
userEvent.click(faveBandItem)
// verify autocomplete has new value
expect(faveBand).toHaveValue('Van Halen')
userEvent, waitFor 및 화면을 Import합니다.
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
여기서의 문제는 자동완성이 기본적으로 포털을 사용하고 옵션을 본문에 렌더링한다는 것입니다.렌더링된 컨테이너에 존재하지 않는 자동 완성을 문서 본문에 렌더링해야 합니다.
옵션 메뉴가 뜨기를 기다렸다가 옵션을 검증할 수 있는 솔루션을 찾을 수 있었습니다.
시뮬레이션된 API에서 반환된 옵션을 사용할 수 있는 주요 단계는 다음과 같습니다.
- 자동 완성 구성 요소의 일부인 "열기" 버튼을 클릭합니다.
- role="option"인 waitFor() 요소가 DOM에 표시되도록 합니다.
또한 테스트 내용이 TypeScript로 작성되어 있기 때문에 HTMLInputElement에 입력 내용을 전달합니다.
test("sends API request on search", async () => {
// arrange
render(<Dropdown />);
// act
const autocomplete = screen.getByRole("combobox");
const input: HTMLInputElement = within(autocomplete).getByLabelText(
"Select a template"
) as HTMLInputElement;
const searchValue = "react-boiler";
const templateValue = "react-boilerplate";
autocomplete.focus();
// open autocomplete dropdown menu
within(autocomplete).getByLabelText("Open").click();
const options = await screen.findAllByRole("option");
// Perform some tests specific to the options provided to the dropdown
// expect(options).toHaveLength(7);
// assign value to input field
fireEvent.change(input, { target: { value: searchValue } });
// navigate to the first item in the autocomplete box
fireEvent.keyDown(autocomplete, { key: "ArrowDown" });
// select the first item
fireEvent.keyDown(autocomplete, { key: "Enter" });
// check the new value of the input field
expect(input.value).toEqual(templateValue);
});
react-testing-library -> user-event를 사용한 저의 견해는 다음과 같습니다.작동시키기 위해 자동 완성 메뉴 옵션에 data-testid를 할당해야 했습니다.
const autoComplete = screen.getByRole("combobox");
expect(autoComplete).toBeVisible();
const autoCompleteDropdown = screen.getByRole("button", { name: "Open" });
// Autocomplete dropdown button.
expect(autoCompleteDropdown).toBeVisible();
userEvent.click(autoCompleteDropdown);
// Autocomplete dropdown view.
expect(screen.getByRole("presentation")).toBeVisible();
// click on administrator menu option in autocomplete.
userEvent.click(screen.getByTestId("option1"));
// imitate click away(this is only required if you have disableCloseOnSelect is enabled.
userEvent.click(document.body);
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
//Verify autocomplete shows the correct value.
expect(screen.getByText("option1")).toBeVisible();
mui 예제에서는 아직 테스트하지 않았지만, 내 앱에서는 테스트했고, 다음 작업이 수행되었습니다.
userEvent.type(screen.getByRole("textbox", {name: /attendees/i}), "Bertrand")
userEvent.click(screen.getByText(/bertrand/i))
여기서는 userEvent)에서.Bertrand
예에서는 '어느 정도 하다' 입니다Schindlers
) 。여기서 이름 붙입니다.screen.getByRole
예에서는 다음과 같습니다).openOnFocus: false
예상 를Schindlers
를 참조해 주세요.
NB에서 했습니다.@mui/material
AutoComplete
에서 이 아니다
제 경우 다음 페이지 버튼을 활성화하기 위해 두 개의 자동 완성 필드에 값을 입력해야 했습니다.나는 상위 정답이 나에게 맞지 않아서 수정했다.
const autocompleteClient = renderResult.getByTestId('client');
const inputClient = autocompleteClient.querySelector('input');
autocompleteClient.focus();
// assign value to input field of client
fireEvent.change(inputClient!!, { target: { value: 'Client 1' } })
// presentation role is for the options of the autocomplete
await renderResult.findAllByRole('presentation');
fireEvent.keyDown(autocompleteClient, { key: 'ArrowDown' })
fireEvent.keyDown(autocompleteClient, { key: 'Enter' })
const autocompleteProposal = renderResult.getByTestId('proposal-no');
const inputProposal = autocompleteProposal.querySelector('input');
autocompleteProposal.focus();
fireEvent.change(inputProposal!!, { target: { value: 'proposal 1' } })
await renderResult.findAllByRole('presentation');
fireEvent.keyDown(autocompleteProposal, { key: 'ArrowDown' })
fireEvent.keyDown(autocompleteProposal, { key: 'Enter' })
expect(screen.getByText('Next')).toBeEnabled();
fireEvent.click(screen.getByText('Next'));
expect(screen.getByText('Generate')).toBeDisabled();
서 ★★★★★renderResult = render(<YourComponent/>);
팝업이 뜨는지 안 뜨는지 확인 안 해도 돼요.이것은, 에 의해서 처리됩니다.Autocomplete
정상적으로 동작할 것으로 기대하고 있습니다. 업은 of of of of of of of of of of of of of of of of의 입니다.option
중요한 것은 옵션입니다.
옵션을 검증하기 위해 다음 두 가지 테스트를 생각할 수 있습니다.
- 사용자가 옵션을 선택했을 때 폼이 올바르게 동작하는지 확인합니다.
- 사용자 유형으로 올바른 옵션 목록이 표시되는지 확인합니다.
폼이 올바르게 동작하는지 확인합니다.
작업을 하려면 먼저 을 합니다.Textbox
올바른 옵션을 클릭합니다.
const user = userEvent.setup();
const { findByLabelText, findByRole } = render(
<Autocomplete
options={["option 1", "option 2"]}
renderInput={(params) => <TextField {...params} label="label" />}
/>)
user.click(getByLabelText("label"))
user.click(getByRole("option", { name: "option 1" }))
사용자가 여러 값을 변경할 때 발생하는 논리를 확인하려면 이 작업을 여러 번 수행할 수 있습니다.
올바른 옵션 목록 확인
옵션 목록이 정적 목록이라면 굳이 테스트하지 않아도 됩니다.는 '할 수 있다.Autocomplete
일을 제대로 할 수 있도록 말이죠.
이러한 유형의 테스트는 백엔드에서 옵션을 가져오거나 입력하는 항목에 따라 동적으로 계산되는 경우에 적합합니다.예를 들어, 구글 플레이스 검색입니다.
이 경우 제시된 모든 옵션을 사용할 수 있습니다.
// Type in something
user.type(getByLabelText("label"), "opt")
// Potentially wait
// Get the now narrows down list of options.
const options = await findAllByRole("option") // or queryAllByRole
여기서는 하나의 옵션 세트만 표시된다고 가정합니다.그러나 자동 완료를 선택하면 새 팝업이 열리면 팝업이 닫히므로 이 방법은 작동합니다.
데이터를 동적으로 취득할 경우 비동기 코드가 정착되어 옵션목록이 갱신될 때까지 테스트에서 기다려야 할 수 있습니다.
p.s. 이걸 알아내는데 반나절이나 걸려서 쓴 거예요.이 질문에 대한 이전의 답변, 블로그 투고 등을 포함한 다른 모든 리소스는 개별 키 누르기, 의존관계 등 구현 세부사항과 너무 결합되어 있었습니다.data-testid
어트리뷰트(최종 수단으로만 사용)
내 테스트의 목적은 내 폼의 동작을 확인하는 것이었다.이상적으로는 사용할 수 있었지만 MUI 자동완성에서는 작동하지 않았습니다.
onInputChange 함수를 사용하여 입력이 변경되었는지 확인할 수 있습니다.
test('my test', () => {
const { container } = render(
<Autocomplete
noOptionsText="no Option"
getOptionLabel={(option) => option.name}
onInputChange={(__, value) => {
//implements logic when value is selected
}}
multiple
id="my-id"
options={[{ name: 'My name 1' }]}
renderInput={(params) => <TextField {...params} label="My Label" />}
/>
)
const input = container.querySelector('#my-id')
fireEvent.change(input, { target: { value: { name: 'My name 1' } } })
})
나는 이것을 해결책으로 찾았다.
const autoComplete = getByLabelText('component-autoComplete');
const input = within(autoComplete).getByRole('textbox');
autoComplete.focus();
fireEvent.change(input, { target: { value: 'mockValue' } });
fireEvent.keyDown(autoComplete, { key: 'ArrowDown' });
fireEvent.keyDown(autoComplete, { key: 'Enter' });
expect(input).toHaveValue('mockValue');
다음과 같이 진행해야 합니다.
- 자동 완료를 클릭합니다.
- 검색 옵션
- 선택 옵션
const label = 'label of your autocomplete'
const textBox = screen.getByRole('textbox', {
name: label,
});
userEvent.click(textBox);
// wait for option to appear
await waitFor(() => {
screen.getByRole('listbox');
});
// grab option
const opt = screen.getByRole('option', {
name: /The Great Dictator/i,
});
// select it
userEvent.click(opt);
언급URL : https://stackoverflow.com/questions/60882089/how-to-test-material-ui-autocomplete-with-react-testing-library
'source' 카테고리의 다른 글
날짜를 기준으로 쿼리를 반환하다 (0) | 2023.03.08 |
---|---|
ui-sref Angularjs 값을 동적으로 설정합니다. (0) | 2023.03.08 |
워드프레스로 생성된 암호를 해독하는 방법 (0) | 2023.03.08 |
SpringBoot의 속성 파일, YAML 파일 및 명령줄 인수 중 우선 순위 (0) | 2023.03.08 |
Jest를 여러 프리셋으로 동시에 사용할 수 있습니까? (0) | 2023.03.08 |