我正在尝试测试一个看起来像这样的简单组件
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import styles from './styles.css'
export class App extends PureComponent {
handleClick = (event) => {
const { loadGreetings } = this.props
loadGreetings()
}
render () {
const { hi } = this.props
return (
<section>
<h1 className={styles.earlyDawn}>{hi}</h1>
<button onClick={this.handleClick}>Handshake</button>
</section>
)
}
}
App.propTypes = {
hi: PropTypes.string,
loadGreetings: PropTypes.func
}
export default App
这是我的测试文件
import React from 'react'
import {App} from './index'
import {shallow} from 'Enzyme'
describe('Testing App container...', () => {
let props
beforeEach(() => {
props = {
loadGreetings: jest.fn().mockName('loadGreetings'),
hi: 'Hi from test'
}
})
test('should handle click on the button', () => {
const wrapper = shallow(<App {...props}/>)
const buttonHi = wrapper.find('button')
const instance = wrapper.instance()
expect(buttonHi.length).toBe(1)
jest.spyOn(instance, 'handleClick')
buttonHi.simulate('click')
expect(props.loadGreetings).toHaveBeenCalled()
expect(instance.handleClick).toHaveBeenCalled()
})
})
所以问题出在第二个toHaveBeenCalled断言始终失败。但是,首先toHaveBeenCalled似乎在工作,这使我感到困扰,因为props.loadGreetings在instance.handleClick内部被调用。您能帮我找出问题所在吗?
依赖项:“反应”:“ 16.9.0”,“反应dom”:“ 16.9.0”,“巴比尔开玩笑”:“ ^ 24.8.0”,“酶”:“ ^ 3.10.0” ,“笑话”:“ ^ 24.8.0”,
答案 0 :(得分:0)
一种更简单的方法是传入一些初始道具,并基于这些初始道具测试您的组件-您还将操纵这些道具以添加更多的断言。
这是一个可行的示例(单击Tests
标签以运行测试):
components / App / index.js
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
class App extends PureComponent {
handleClick = () => {
this.props.loadGreetings();
};
render() {
const { message } = this.props;
return (
<section className="app">
<h1>{message}</h1>
<button onClick={this.handleClick}>Handshake</button>
</section>
);
}
}
App.propTypes = {
message: PropTypes.string,
loadGreetings: PropTypes.func
};
export default App;
components / App / App.test.js
import React from "react";
import { shallow } from "enzyme";
import App from "./index";
// define the passed in function here for easier testing below
const loadGreetings = jest.fn();
// initial props to pass into 'App'
const initProps = {
message: "hi",
loadGreetings
};
describe("Testing App container...", () => {
let wrapper;
beforeEach(() => {
// this resets the wrapper with initial props defined above
wrapper = shallow(<App {...initProps} />);
});
afterEach(() => {
// this clears any calls to the mocked function
// and thereby resets it
loadGreetings.mockClear();
});
it("renders without errors", () => {
expect(wrapper.find(".app").exists()).toBeTruthy();
expect(loadGreetings).toHaveBeenCalledTimes(0);
});
it("initially renders a 'hi' message and then a 'goodbye' message", () => {
expect(wrapper.find("h1").text()).toEqual("hi");
// manipulating the initial 'message' prop
wrapper.setProps({ message: "goodbye" });
expect(wrapper.find("h1").text()).toEqual("goodbye");
});
it("should call 'loadGreetings' when the 'Handshake' button is clicked", () => {
// since we passed in 'loadGreetings' as a jest function
// we expect it to be called when the 'Handshake' button is
// clicked
wrapper.find("button").simulate("click");
expect(loadGreetings).toHaveBeenCalledTimes(1);
});
});
不推荐(请参见下文),但是您可以监视类方法-您必须使用实例,强制更新实例,然后手动wrapper.instance().handleClick()
或通过间接调用handleClick方法某些元素的事件处理程序:wrapper.find("button").simulate("click")
或wrapper.find("button").props().onClick()
。
我不推荐这种测试策略的原因是您正在测试React实现(测试元素的事件处理程序是否调用了您的回调函数)。相反,可以通过断言是否调用了prop函数和/或发生prop / state更改来避免这种情况。这是测试组件的更标准,更直接的方法,因为这就是我们所关心的。我们关心道具和/或状态会根据用户的某些操作而变化。换句话说,通过对所谓的“ loadGreetings”道具进行断言,我们已经在测试onClick事件处理程序的工作原理。
工作示例:
App.test.js (与上述测试相同,但该测试除外):
it("should call 'loadGreetings' when the 'Handshake' button is clicked", () => {
const spy = jest.spyOn(wrapper.instance(), "handleClick"); // spying on the method class
wrapper.instance().forceUpdate(); // required to ensure the spy is placed on the method
wrapper.find("button").simulate("click");
expect(spy).toHaveBeenCalledTimes(1);
const mockedFn = jest.fn(); // setting the method as a mocked fn()
wrapper.instance().handleClick = mockedFn;
wrapper.instance().forceUpdate(); // required to update the method instance with a mocked fn
wrapper.find("button").simulate("click");
expect(mockedFn).toHaveBeenCalledTimes(1);
});