В чем смысл модульного тестирования redux-saga watchers?

чтобы получить 100% покрытие моих файлов саги, я изучаю, как тестировать наблюдателей.

я гуглил вокруг, есть несколько ответов о том, как проверить наблюдателей. То есть, саги, которые делают takeEvery или takeLatest.

однако все методы тестирования, похоже, в основном копируют реализацию. Так какой смысл писать тест, если это одно и то же?

пример:

// saga.js

import { delay } from 'redux-saga'
import { takeEvery, call, put } from 'redux-saga/effects'
import { FETCH_RESULTS, FETCH_COMPLETE } from './actions'

import mockResults from './tests/results.mock'

export function* fetchResults () {
  yield call(delay, 1000)
  yield put({ type: FETCH_COMPLETE, mockResults })
}

export function* watchFetchResults () {
  yield takeEvery(FETCH_RESULTS, fetchResults)
}

метод испытания 1:

import { takeEvery } from 'redux-saga/effects'
import { watchFetchResults, fetchResults } from '../sagas'
import { FETCH_RESULTS } from '../actions'

describe('watchFetchResults()', () => {
    const gen = watchFetchResults()
    // exactly the same as implementation
    const expected = takeEvery(FETCH_RESULTS, fetchResults)
    const actual = gen.next().value

    it('Should fire on FETCH_RESULTS', () => {
      expect(actual).toEqual(expected)
    })
  })

метод испытания 2: с помощником, как Redux Saga План Тестирования
Это другой способ написания, но опять же мы делаем в основном то же самое, что и реализация.

import testSaga from 'redux-saga-test-plan'
import { watchFetchResults, fetchResults } from '../sagas'
import { FETCH_RESULTS } from '../actions'

it('fire on FETCH_RESULTS', () => {
  testSaga(watchFetchResults)
    .next()
    .takeEvery(FETCH_RESULTS, fetchResults)
    .finish()
    .isDone()
})

вместо этого я хотел бы просто знать, если watchFestchResults принимает каждый FETCH_RESULTS. Или даже если он выстрелит!--6-->. Независимо от того, как это следует.

или это действительно так?

2 ответов


похоже, что точка тестирования их заключается в достижении 100% тестового покрытия.

есть некоторые вещи, которые вы можете модульный тест, но это сомнительно, если вы должны.

Мне кажется, что эта ситуация может быть лучшим кандидатом для тестирования "интеграция". Что-то, что не проверяет просто один метод, но как несколько методов работают вместе в целом. Возможно, вы могли бы вызвать действие, которое запускает редуктор, который использует вашу сагу, а затем проверить магазин в результате изменения? Это было бы гораздо более значимым, чем проверка саги в одиночку.


Я согласен с Джоном Мейером ответ что это лучше подходит для интеграционного теста, чем для модульного теста. Это вопрос является самым популярным в GitHub на основе голосов. Я бы рекомендовал прочитать его.

одно из предложений-использовать redux-saga-tester пакет, созданный открывателем проблемы. Это помогает создать начальное состояние, запустить помощников saga (takeEvery, takeLatest), отправлять действия, которые слушает saga, наблюдать за состояние, получить историю действий и слушать для конкретных действий, чтобы произойти.

Я использую его с axios-макет-адаптер, но в базе кода есть несколько примеров использования НОК.

Сага

import { takeLatest, call, put } from 'redux-saga/effects';
import { actions, types } from 'modules/review/reducer';
import * as api from 'api';

export function* requestReviews({ locale }) {
  const uri = `/reviews?filter[where][locale]=${locale}`;
  const response = yield call(api.get, uri);
  yield put(actions.receiveReviews(locale, response.data[0].services));
}

// Saga Helper
export default function* watchRequestReviews() {
  yield takeLatest(types.REVIEWS_REQUEST, requestReviews);
}

тестовый пример с использованием Jest

import { takeLatest } from 'redux-saga/effects';
import { types } from 'modules/review/reducer';
import SagaTester from 'redux-saga-tester';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';

import watchRequestReviews, { requestReviews } from '../reviews';

const mockAxios = new MockAdapter(axios);

describe('(Saga) Reviews', () => {
  afterEach(() => {
    mockAxios.reset();
  });

  it('should received reviews', async () => {
    const services = [
      {
        title: 'Title',
        description: 'Description',
      },
    ];
    const responseData = [{
      id: '595bdb2204b1aa3a7b737165',
      services,
    }];

    mockAxios.onGet('/api/reviews?filter[where][locale]=en').reply(200, responseData);

    // Start up the saga tester
    const sagaTester = new SagaTester({ initialState: { reviews: [] } });

    sagaTester.start(watchRequestReviews);

    // Dispatch the event to start the saga
    sagaTester.dispatch({ type: types.REVIEWS_REQUEST, locale: 'en' });

    // Hook into the success action
    await sagaTester.waitFor(types.REVIEWS_RECEIVE);

    expect(sagaTester.getLatestCalledAction()).toEqual({
      type: types.REVIEWS_RECEIVE,
      payload: { en: services },
    });
  });
});