#javascript #reactjs #jestjs #enzyme
#javascript #reactjs #jestjs #фермент
Вопрос:
Ниже у меня есть пример компонента React, который ведет себя следующим образом —
- Когда
showDiv
состояниеfalse
(начальное состояние), оно ничего не отображает. - Когда
showDiv
состояние обновляетсяtrue
(возможно, с помощью какого-либо внешнего компонента, redux и т. Д.), Оно отображает a<div>
с помощью одного радиовхода.
Я также включил тесты Jest:
import { expect } from 'chai';
import { mount } from 'enzyme';
import PropTypes from 'prop-types';
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { showDiv: false };
}
render() {
const { showDiv } = this.state;
if (!showDiv) { return null; }
return (
<div className='my-component'>
<input type="radio" value="yes" id="foo" checked={true} />
</div>
);
}
}
describe('<MyComponent />', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<MyComponent />);
});
it('should render the div', () => {
// This INCORRECTLY passes
expect(wrapper.find('#foo').at(0)).to.not.be.null;
expect(wrapper.find('#foo').at(0)).to.be.checked;
});
});
Я написал тест, чтобы убедиться, что <input>
он существует и что он есть checked
, и тест пройден.
Однако при первоначальном монтировании / загрузке компонента это <input>
не должно отображаться (поскольку showDiv
есть false
), так почему тест проходит?
Я даже вывожу состояние компонента с console.log(wrapper.find('#foo').at(0).debug());
помощью и могу убедиться, что ничего не отображается… но он все равно проходит.
$ npx jest --no-cache --config test/js/jest.config.js test/js/components/test.spec.jsx
● Console
console.log test/js/components/test.spec.jsx:34
Есть ли причина, по которой это дает ложное срабатывание? В частности, почему find()
возвращает элементы, которые не существуют, но все же проходят проверки (например .to.be.checked
)
Комментарии:
1. Может ли быть проблема с индексацией и / или проблема с управлением памятью низкого уровня? Я не знаком с «Enzyme», это просто «общие предположения»…
Ответ №1:
Я лично никогда не работал с chai
, но это может потребовать некоторой дополнительной настройки для работы React (я знаю, что для jest с enzyme требуется адаптер). На той же ноте я бы лично рекомендовал просто использовать Jest поверх чая, так как он уже завернут поверх энзима… но в конечном итоге зависит от ваших предпочтений, какой набор тестов использовать.
Примечание: популярным дополнением к jest является jest-enzyme, в котором есть такие методы утверждения, как toExist()
и toBeChecked()
.
Рабочая демонстрация с использованием Jest (переключение с Browser
вкладки на Tests
вкладку для выполнения утверждений):
App.test.js
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import MyComponent from "./App";
configure({ adapter: new Adapter() });
describe("<MyComponent />", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<MyComponent />);
});
it("initially displays a button", () => {
expect(wrapper.find("#show-radio")).toHaveLength(1);
});
it("renders the 'my-component' div", () => {
wrapper.find("#show-radio").simulate("click");
expect(wrapper.find(".my-component")).toHaveLength(1);
expect(wrapper.find("#foo").props().checked).toBeFalsy();
});
it("selects a radio", () => {
wrapper.find("#show-radio").simulate("click");
wrapper.find("#foo").prop("onChange")({ target: { value: "1" } });
wrapper.update();
expect(wrapper.find("#foo").props().checked).toBeTruthy();
});
it("resets radio value", () => {
wrapper.find("#show-radio").simulate("click");
wrapper.find("#foo").prop("onChange")({ target: { value: "1" } });
wrapper.find("#reset").simulate("click");
wrapper.update();
expect(wrapper.find("#foo").props().checked).toBeFalsy();
});
it("hides radios", () => {
wrapper.find("#show-radio").simulate("click");
wrapper.find("#close-radio").simulate("click");
wrapper.update();
expect(wrapper.find("#show-radio")).toHaveLength(1);
expect(wrapper.find(".my-component")).toHaveLength(0);
});
});
App.js
import React from "react";
import "./styles.css";
export default class MyComponent extends React.Component {
state = { showDiv: false, value: 0 };
setValue = ({ target: { value } }) => this.setState({ value });
resetValue = () => this.setState({ value: 0 });
toggleRadio = () =>
this.setState((prevState) => ({ showDiv: !prevState.showDiv }));
render() {
const { showDiv, value } = this.state;
if (!showDiv) {
return (
<button id="show-radio" type="button" onClick={this.toggleRadio}>
Toggle
</button>
);
}
return (
<div className="my-component">
<label htmlFor="foo">
<input
type="radio"
id="foo"
value="1"
checked={value === "1"}
onChange={this.setValue}
/>
1
</label>
<label htmlFor="bar">
<input
type="radio"
id="bar"
value="2"
checked={value === "2"}
onChange={this.setValue}
/>
2
</label>
<br />
<button id="reset" type="button" onClick={this.resetValue}>
Reset
</button>
<button id="close-radio" type="button" onClick={this.toggleRadio}>
Close
</button>
</div>
);
}
}