Вот решение, структура папок:
.
├── 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