自动化测试的使用示例
单元测试
react 17 使用 @wojtekmaj/enzyme-adapter-react-17
对方法进行 wrap
点击查看代码
'should call method once with argument': function () {
var object = { method: function () {} };
var spy = sinon.spy(object, 'method');
object.method(1);
assert(spy.withArgs(1).calledOnce);
}
import { shallow } from 'enzyme'
import sinon from 'sinon';
describe('测试 demo', () => {
const onButtonClick = sinon.spy()
const children = (
<div className="demo" onClick={onButtonClick}>
demo
</div>
)
const warrper = shallow(<div>{children}</div>)
test('测试渲染dom结构', () => {
expect(warrper.find('.demo')).toHaveLength(1)
})
test('测试子元素时', () => {
expect(warrper.contains(children)).toEqual(true)
})
test('测试点击事件', () => {
warrper.find('.demo').simulate('click')
expect(onButtonClick).toHaveProperty('callCount', 1)
})
})
class类组件
点击查看代码
export const findTestWrapper = (wrapper, tag) => {
return wrapper.find(`[data-test="${tag}"]`);
}
describe('输入框内容随用户输入变化', () => {
const wrapper = shallow(<Header />);
const inputElem = findTestWrapper(wrapper, 'input');
const userInput = '今天要学习 Jest';
inputElem.simulate('change', {
target: { value: userInput}
});
expect(wrapper.state('value')).toEqual(userInput);
});
hooks function组件
点击查看代码
describe('<TestComponent />', () => {
let wrapper;
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, 'useState')
useStateSpy.mockImplementation((init) => [init, setState]);
beforeEach(() => {
wrapper = Enzyme.shallow(<TestComponent />);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Count Up', () => {
it('calls setCount with count + 1', () => {
wrapper.find('#count-up').props().onClick();
expect(setState).toHaveBeenCalledWith(1);
});
});
describe('Count Down', () => {
it('calls setCount with count - 1', () => {
wrapper.find('#count-down').props().onClick();
expect(setState).toHaveBeenCalledWith(-1);
});
});
describe('Zero', () => {
it('calls setCount with 0', () => {
wrapper.find('#zero-count').props().onClick();
expect(setState).toHaveBeenCalledWith(0);
});
});
});
describe('模拟React的useState 方法,并调用', () => {
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, 'useState');
const useStateMock: any = (initState: any) => [initState, setState];
useStateSpy.mockImplementation(useStateMock);
const wrapper = shallow(<LoginStatus />);
React.useState('150');
expect(useStateSpy).toHaveBeenCalledWith('150');
expect(useStateSpy).toHaveBeenCalled();
});
测试 dva effects
点击查看代码
import { expect } from 'chai';
import { runSaga, effects } from 'dva/saga';
describe('Account Manage', function() {
describe('info', function() {
const {
state,
effects: {
edit
}
} = require('../src/account/models/info');
it('should dispatch saveInfo action when editing', async function() {
const dispatched = [];
const payload = { username: 'name' };
const result = await runSaga({
dispatch: (action) => dispatched.push(action),
getState: () => state
}, edit, { payload }, effects).done;
expect(dispatched).toHaveLengthOf(1);
expect(dispatched[0]).toHaveProperty('type', 'saveInfo');
});
});
});
组件测试
Enzyme
- shallow:浅渲染,可以用simulate进行交互模拟
- render:静态渲染,需要测试对子组件进行判断
- mount:完全渲染,可以用simulate进行交互模拟,需要测试组件的生命周期。
点击查看代码
import { expect } from 'chai';
import React from 'react';
import { mount } from 'enzyme';
import { effects } from 'dva/saga';
const { put } = effects;
describe('Banner', function() {
const Banner = require('../src/components/Banner');
it('should render correctly', async function() {
const wrapper = mount(<Banner dispatch={put}/>);
// wait async request
await new Promise((resolve) => setTimeout(resolve, 2000));
expect(wrapper.state('bannerData')).to.have.lengthOf.at.least(1);
expect(wrapper.find('.slick-list')).to.have.lengthOf.above(1);
});
});
集成测试
编写第一个 React 集成测试中的例子
git clone git@github.com:montezume/calculator.git
点击查看代码
import React from "react";
import userEvent from "@testing-library/user-event";
import { render, screen } from "@testing-library/react";
import App from "./App";
describe("negative numbers", () => {
describe("when we add -5 to 2", () => {
it("equals -3", () => {
render(<App />);
userEvent.click(screen.getByRole("button", { name: "5" }));
userEvent.click(screen.getByRole("button", { name: "+/-" }));
userEvent.click(screen.getByRole("button", { name: "+" }));
userEvent.click(screen.getByRole("button", { name: "2" }));
userEvent.click(screen.getByRole("button", { name: "=" }));
expect(screen.getByText("-3")).toBeInTheDocument();
// clear the screen
userEvent.click(screen.getByRole("button", { name: "AC" }));
expect(screen.queryByText("-3")).not.toBeInTheDocument();
});
});
});
E2E 测试
点击查看代码
/ launch server
import ready from '../../server';
import { expect } from 'chai';
import puppeteer from 'puppeteer';
describe('Account', function() {
// set a long timeout
this.timeout(60000);
describe('log in', function() {
it('should log in correctly', async function() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// wait dev server ready
await ready();
await page.goto('http://localhost:8000/account/login', { 'waitUntil': 'networkidle0' });
await page.type('#username', 'smalldragonluo');
await page.type('#password', 'pwd');
await page.click('#submit');
// ...
const result = await page.$eval('#result', (element) => {
return element.innerHTML
})
expect(result).to.equal('Success!');
await browser.close();
});
});
});