Я впервые работаю с тестами, и у меня есть прием для тестирования компонентов пользовательского интерфейса. Сейчас я пытаюсь протестировать класс, в котором есть статические методы. Он также содержит параметры.

Смотрите класс:

import UserInfoModel from '../models/UserInfo.model';
import ApiClient from './apiClient';
import ApiNormalizer from './apiNormalizer';
import Article from '../models/Article.model';
import Notification from '../models/Notification.model';
import Content from '../models/Link.model';

export interface ResponseData {
  [key: string]: any;
}

export default class ApiService {
  static makeApiCall(
    url: string,
    normalizeCallback: (d: ResponseData) => ResponseData | null,
    callback: (d: any) => any
  ) {
    return ApiClient.get(url)
      .then(res => {
        callback(normalizeCallback(res.data));
      })
      .catch(error => {
        console.error(error);
      });
  }


  static getProfile(callback: (a: UserInfoModel) => void) {
    return ApiService.makeApiCall(`profile`, ApiNormalizer.normalizeProfile, callback);
  }
}

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

// @ts-ignore
import moxios from 'moxios';
import axios from 'axios';
import { baseURL } from './apiClient';
import { dummyUserInfo } from './../models/UserInfo.model';

describe('apiService', () => {
  let axiosInstance: any;

  beforeEach(() => {
    axiosInstance = axios.create();
    moxios.install();
  });

  afterEach(() => {
    moxios.uninstall();
  });

  it('should perform get profile call', done => {
    moxios.stubRequest(`${baseURL.DEV}profile`, {
      status: 200,
      response: {
        _user: dummyUserInfo
      }
    });

    axiosInstance
      .get(`${baseURL.DEV}profile`)
      .then((res: any) => {
        expect(res.status).toEqual(200);
        expect(res.data._user).toEqual(dummyUserInfo);
      })
      .finally(done);
  });
});

Я использую moxios для тестирования материала axios -> https://github.com/axios/moxios

Итак, что может быть правильным способом проверить этот класс с его методами?

6
Non 13 Авг 2019 в 07:58

2 ответа

Термин юнит-тест говорит само за себя, что вы тестируете юнит. Функция в полной изоляции. Любые внешние зависимости высмеиваются. Здесь, если вы тестируете функцию makeApiCall, вам нужно будет заглушить ее параметры, а затем смоделировать обещание ApiClient и ожидать, что функция вернет все, что вы ожидаете, относительно параметров макета и заглушки.

Одна вещь, которую люди обычно забывают и которая является наиболее важной, - это проверка негативных случаев функции. Что произойдет, если ваша функция выдаст ошибку, она сломает приложение. Как ведет себя ваша функция в случае сбоя. Тесты написаны, чтобы не нарушать изменения в приложении.

Вот лучшее руководство по тестированию асинхронных функций в JEST, примеры кодирования:

https://www.leighhalliday.com/mocking-axios-in-jest-testing-async-functions

Надеюсь это поможет

ОБНОВЛЕНИЕ

Издевайся над своим ApiClient

Для пропуска:

jest.mock('./apiClient', () => {
  get: jest.fn(() => Promise.resolve(data)) // for pass case
})

Для случая сбоя:

jest.mock('./apiClient', () => {
  get: jest.fn(() => Promise.reject(false)) // for fail case
})

Теперь вызовите ваш makeApiCall для обоих случаев один раз для успеха и один раз для неудачи.

для случая сбоя:

const makeCall = await makeApiCall( <your stub params here> )
expect(makeCall).toThrowError() // note here you can check whatever you have done to handle error. ToThrowError is not a built-in function but just for understanding

В основном я проводил тестирование в Jasmine, так что этот последний фрагмент кода является своего рода псевдо-кодом.

6
Omair Nabiel 21 Авг 2019 в 09:26

Я предполагаю, что вы спрашиваете, как проверить ApiService. Если это так, то насмешка над тем, что вы хотите протестировать, сделает тестирование модуля бессмысленным.

То, что я ожидал бы, следующие пункты

  1. Вы просто хотите проверить логику в своем собственном классе, а не в библиотеке.
  2. Вы не хотите делать реальный сетевой запрос, это приводит к спаму на сервере и замедляет выполнение теста.

Если это так, то вы должны смоделировать некоторую библиотеку, чтобы контролировать их поведение и посмотреть, как ваш класс ведет себя в этих обстоятельствах. И макетируйте любую операцию, которая включает сетевой ввод-вывод, сделайте ваш тест более быстрым и менее зависимым от внешних ресурсов.

Есть несколько вещей, которые вы можете проверить с некоторыми зависимостями:

  1. делегирование, например axios вызывается один раз с правильным параметром?
  2. напрямую смоделируйте поведение библиотеки, в вашем случае используя maxios.
import ApiService, { baseURL } from './apiClient';


describe('ApiService', () => {
  let axiosInstance: any;

  beforeEach(() => {
    axiosInstance = axios.create();
    moxios.install();
  });

  afterEach(() => {
    moxios.uninstall();
  });

  // usually 1 test suite for each method
  describe('#getProfile', (done) => {
    // mocking behaviour
    it('should perform get profile call', () => {
      moxios.stubRequest(`${baseURL.DEV}profile`, {
        status: 200,
        response: {
          _user: dummyUserInfo
        }
      });

      ApiService.getProfile((profile) => {
        expect(profile).toEqual(dummyUserInfo); // you get what i mean
        done();
      });
    });

    // directly mock axios
    it('delegates to axios', (done) => {
      // you should put this to the top to avoid confusion, it will be hoisted
      jest.mock('axios', () => ({
        create: jest.fn(() => ({
          get: jest.fn(() => Promise.resolve()),
        })),
      }));

      ApiService.getProfile((profile) => {
        // do some assertion
        expect(axiosInstance.get).toHaveBeenCalledTimes(1);
        expect(axiosInstance.get).toHaveBeenCalledWith(url, someParam, youGetIt);
        done();
      });
    });

    // rmb to test some error case
    it('should throw when param is not correct', (done) => { ... });
  });
});
1
seanplwong 27 Авг 2019 в 10:48