[英]Code coverage concern on promise/asynchronous unit testing using nockjs and jest
[英]How to code coverage a promise inside functions while unit testing using Jest
在使用笑話進行單元測試時,如何在 function 中編寫覆蓋代碼並捕獲 promise 的 function ? 請看下面的代碼。
服務.js
export const userLogin = data => {
return AjaxService.post(
"http://localhost/3000/signin", data
).then(
res => {
return res.data;
},
error => {
return error.response.data;
}
);
};
AjaxService.js
export const AjaxService = {
post: (url, data, headers) => {
return axios({
method: "POST",
url: url,
headers: headers || { "content-type": "application/json" },
data: data
});
}
}
Example.js
class Login extends Component {
handleSubmit = (event) => {
if (this.props.handleSubmit) this.props.handleSubmit();
this.setState({isLoggedIn: true})
userLogin().then((res) => {
// when promise resolve
var response = res;
}, (err) => {
// when promise reject
var error = err;
})
}
render() {
return (
<form id="login-form" onSubmit={(e) => this.handleSubmit(e)} >
<input type="username" />
<input type="password" />
<button type="submit">Login</button>
</form>
)
}
}
Example.test.js
it("test login form submit ", () => {
wrapper = shallow(<Login />);
let instance = wrapper.instance(); // get class instance
instance.handleSubmit(); // will trigger component method
let actualVal = wrapper.state().isLoggedIn; // get state key value
expect(true).to.eql(actualVal);
});
在 Jest 中使用--coverage
生成覆蓋率報告后
我們可以看到 promise 成功和錯誤 function 中的代碼沒有作為單元測試的一部分被覆蓋。 所以請幫助解決這個問題。 謝謝。
這是解決方案,文件夾結構:
.
├── ajaxService.ts
├── example.spec.tsx
├── example.tsx
└── service.ts
ajaxService.ts
:
import axios from 'axios';
export const AjaxService = {
post: (url, data, headers?) => {
return axios({
method: 'POST',
url,
headers: headers || { 'content-type': 'application/json' },
data
});
}
};
service.ts
:
import { AjaxService } from './ajaxService';
export const userLogin = data => {
return AjaxService.post('http://localhost/3000/signin', data).then(
res => {
return res.data;
},
error => {
return error.response.data;
}
);
};
example.tsx
:
import React, { Component } from 'react';
import { userLogin } from './service';
export interface ILoginProps {
handleSubmit(): void;
}
interface ILoginState {
isLoggedIn: boolean;
}
export class Login extends Component<ILoginProps, ILoginState> {
constructor(props: ILoginProps) {
super(props);
this.state = {
isLoggedIn: false
};
}
public handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (this.props.handleSubmit) {
this.props.handleSubmit();
}
this.setState({ isLoggedIn: true });
const data = {};
return userLogin(data).then(
res => {
console.log(res);
},
err => {
console.error(err);
}
);
}
public render() {
return (
<form id="login-form" onSubmit={e => this.handleSubmit(e)}>
<input type="username" />
<input type="password" />
<button type="submit">Login</button>
</form>
);
}
}
example.spec.tsx
:
import React from 'react';
import { shallow } from 'enzyme';
import { Login, ILoginProps } from './example';
import * as service from './service';
describe('Login', () => {
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
const mockedProps: ILoginProps = { handleSubmit: jest.fn() };
const mockedFormEvent = { preventDefault: jest.fn() };
const mockedUserLoginResponse = 'mocked data';
const mockedUserLoginError = new Error('database error');
it('test login form submit - 1', done => {
const userLoginSpy = jest.spyOn(service, 'userLogin').mockResolvedValueOnce(mockedUserLoginResponse);
const logSpy = jest.spyOn(console, 'log');
const wrapper = shallow(<Login {...mockedProps}></Login>);
wrapper.find('form').simulate('submit', mockedFormEvent);
setImmediate(() => {
expect(mockedFormEvent.preventDefault).toBeCalledTimes(1);
expect(mockedProps.handleSubmit).toBeCalledTimes(1);
expect(wrapper.state('isLoggedIn')).toBeTruthy();
expect(userLoginSpy).toBeCalledWith({});
expect(logSpy).toBeCalledWith(mockedUserLoginResponse);
done();
});
});
it('test login form submit - 2', async () => {
const userLoginSpy = jest.spyOn(service, 'userLogin').mockResolvedValueOnce(mockedUserLoginResponse);
const logSpy = jest.spyOn(console, 'log');
const wrapper = shallow(<Login {...mockedProps}></Login>);
await (wrapper.instance() as any).handleSubmit(mockedFormEvent);
expect(mockedFormEvent.preventDefault).toBeCalledTimes(1);
expect(mockedProps.handleSubmit).toBeCalledTimes(1);
expect(wrapper.state('isLoggedIn')).toBeTruthy();
expect(userLoginSpy).toBeCalledWith({});
expect(logSpy).toBeCalledWith(mockedUserLoginResponse);
});
it('test login error - 1', done => {
const userLoginSpy = jest.spyOn(service, 'userLogin').mockRejectedValueOnce(mockedUserLoginError);
const errorLogSpy = jest.spyOn(console, 'error');
const wrapper = shallow(<Login {...mockedProps}></Login>);
wrapper.find('form').simulate('submit', mockedFormEvent);
setImmediate(() => {
expect(mockedFormEvent.preventDefault).toBeCalledTimes(1);
expect(mockedProps.handleSubmit).toBeCalledTimes(1);
expect(wrapper.state('isLoggedIn')).toBeTruthy();
expect(userLoginSpy).toBeCalledWith({});
expect(errorLogSpy).toBeCalledWith(mockedUserLoginError);
done();
});
});
});
帶有覆蓋率報告的單元測試結果:
PASS src/stackoverflow/58110463/example.spec.tsx
Login
✓ test login form submit - 1 (16ms)
✓ test login form submit - 2 (3ms)
✓ test login error - 1 (10ms)
console.log node_modules/jest-mock/build/index.js:860
mocked data
console.log node_modules/jest-mock/build/index.js:860
mocked data
console.error node_modules/jest-mock/build/index.js:860
Error: database error
at Suite.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58110463/example.spec.tsx:14:32)
at addSpecsToSuite (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmine/Env.js:496:51)
at Env.describe (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmine/Env.js:466:11)
at describe (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmine/jasmineLight.js:81:18)
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58110463/example.spec.tsx:6:1)
at Runtime._execModule (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runtime/build/index.js:888:13)
at Runtime._loadModule (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runtime/build/index.js:577:12)
at Runtime.requireModule (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runtime/build/index.js:433:10)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:201:13
at Generator.next (<anonymous>)
at asyncGeneratorStep (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:27:24)
at _next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:47:9)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:52:7
at new Promise (<anonymous>)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:44:12
at _jasmine (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:206:19)
at jasmine2 (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/index.js:60:19)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runner/build/runTest.js:385:24
at Generator.next (<anonymous>)
at asyncGeneratorStep (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runner/build/runTest.js:161:24)
at _next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-runner/build/runTest.js:181:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 86.21 | 50 | 63.64 | 84.62 | |
ajaxService.ts | 66.67 | 0 | 0 | 66.67 | 5 |
example.tsx | 100 | 75 | 100 | 100 | 21 |
service.ts | 40 | 100 | 0 | 40 | 4,6,9 |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 3.035s, estimated 6s
HTML 覆蓋率報告:
源代碼: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58110463
你只需要使用async await
sintax:
it("test login form submit ", async () => {
wrapper = shallow(<Login />);
let instance = wrapper.instance(); // get class instance
await instance.handleSubmit(); // will trigger component method
let actualVal = wrapper.state().isLoggedIn; // get state key value
expect(true).to.eql(isLoggedIn);
});
然后,您的測試將“等待”,直到 promise 被解決或拒絕,它將在then
或catch
內運行。 您可以在此處了解有關 Jest 如何管理異步代碼的更多信息。
另外,如果你要處理很多承諾,我建議你看看等待期待的package。 它真的可以幫助您編寫測試代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.