Как кодировать покрытие обещания внутри функций при модульном тестировании с использованием Jest - PullRequest
0 голосов
/ 26 сентября 2019

Как написать код покрытия и поймать функцию обещания внутри функции во время модульного тестирования с использованием jest?Пожалуйста, смотрите код ниже.

Service.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);
  });

После формирования отчета о покрытии с использованием --coverage в Jest

enter image description here

МыМожно увидеть, что код внутри обещания успеха и функции ошибок не рассматривается как часть модульного тестирования.Поэтому, пожалуйста, помогите охватить это.Спасибо.

Ответы [ 2 ]

0 голосов
/ 26 сентября 2019

Вам просто нужно использовать синтаксис async await:

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);
  });

Затем ваш тест будет "ждать", пока обещание не будет разрешено или отклонено, и будет выполняться внутри then или catch.Вы можете узнать больше о том, как Jest управляет асинхронным кодом здесь .

Кроме того, если вы собираетесь работать с большим количеством обещаний, я рекомендую вам взглянуть на ожидающий пакет.Это действительно может помочь вам кодировать ваши тесты.

0 голосов
/ 26 сентября 2019

Вот решение, структура папок:

.
├── 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:

enter image description here

исходный код: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58110463

...