I have this functional component.
Search.js
function Search() {
const [term, setTerm] = useState('sun');
function handleOnChange(e) {
if (!e.target.value) {
return false;
}
setTerm(e.target.value);
return true;
}
return <input type="text" onChange={handleOnChange} placeholder="Search" />
}
Search.test.js
import { render, fireEvent } from '@testing-library/react';
import Search from '.';
describe('when type a valid term', () => {
it('update the state', () => {
const { getByPlaceholderText } = render(<Search />;
// this doesn't work. The handleOnChange method is private. How to deal with this?
const handlerSpy = jest.spyOn(Search, 'handleOnChange');
fireEvent.click(getByPlaceholderText(/search/i), { target: { value: 'moon' } });
expect(handlerSpy).toHaveReturnedWith(true);
});
});
I don't know if I'm trying the wrong approach. I just need to test what happens if the user type an empty term. Appreciate any suggestion.
Please, if you have a better answer left it here. After a search for different approaches, I realized another way to test it.
First, I attached the current state to the value attribute of my search field.
This way, I can check if the attribute value of my search field changes accordling.
import { render, fireEvent } from '@testing-library/react';
import Search from '.';
describe('when type a valid term', () => {
it('update the state', () => {
const { getByPlaceholderText } = render(<Search />;
const inputField = getByPlaceholderText(/search/i);
fireEvent.change(inputField, { target: { value: 'moon' } });
expect(inputField).toHaveValue('moon');
});
});
It's possible to write a snapshot test as well.
Your current approach is closer to how tests were done in enzyme (test implementation details). I'd recommend checking the documentation of testing-library https://testing-library.com/docs/intro#the-problem
You should be testing as if your final user was interacting with your app. A more appropriate test might be:
describe('when no search is present', () => {
it('displays placeholder text', () => {
const { getByPlaceholderText } = render(<Search />;
const searchInput = getByPlaceholderText(/search/i)
expect(searchInput).toBeInTheDocument()
});
});
testing this way will give you the confidence you need and also code coverage
You struggle to test it because you're testing to many things at the same time.
Here's a solution where I decouple your state from your rendering.
I converted your component to a class component so now you can have access to state and methods using enzyme .
Note: your example is very simple and these tests might sound overkill. But it's a nice example to understand what you are testing and what tool you can use for each test case.
As a general advice, when you struggle to test something, it's usually because you're testing too many things .
And if you're only testing one thing, then maybe you have too many thing at the same place .
import React from "react";
export default class Search extends React.Component {
constructor(props) {
super(props);
this.handleOnChange = this.handleOnChange.bind(this);
this.state = {
term: ''
}
}
handleOnChange(value) {
if (!value) {
return false;
}
this.setState({term: value});
return true;
}
render() {
return <SearchInput handleOnChange={this.handleOnChange} />
}
}
export function SearchInput(props) {
return <input data-testid="search-input" type="text" placeholder="Search"
onChange={e => props.handleOnChange(e.target.value)} />
}
Now we can test each feature independently.
import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {shallow} from "enzyme";
import Search, {SearchInput} from "./App";
describe('Search section', () => {
describe("Search", () => {
const searchWrapper = shallow(<Search />);
it('should update the term on change', () => {
searchWrapper.instance().handleOnChange("test-input");
expect(searchWrapper.instance().state.term).toBe("test-input");
});
it("should render the search input with handleOnChange", () => {
const searchInput = searchWrapper.find(SearchInput);
expect(searchInput.props().handleOnChange).toBe(searchWrapper.instance().handleOnChange);
});
});
describe("Search input", () => {
it("should call handleOnChange callback when typing", () => {
const onChangeMock = jest.fn();
const searchInput = render(<SearchInput handleOnChange={onChangeMock} />);
userEvent.type(searchInput.getByTestId('search-input'), 'test input');
expect(onChangeMock).toBeCalledWith('test input');
});
});
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.