2017-03-06 29 views
7

Aby uzyskać 100% pokrycia moich plików Saga, zastanawiam się, jak przetestować obserwatorów.Jaki jest sens testowania redux-saga jednostki?

byłem googling wokół, istnieje kilka odpowiedzi, w jaki sposób przetestować obserwatorów. To znaczy, saga, która robi takeEvery lub.

Jednak wszystkie metody testowania wydają się zasadniczo skopiować implementację. Jaki jest sens pisania testu, jeśli jest taki sam?

Przykład:

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

Metoda badawcza 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) 
    }) 
    }) 

metoda Test 2: z pomocnika, jak Redux Saga Test Plan
To inny sposób pisania, ale znowu robimy w zasadzie takie same jako wdrożenie.

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

Zamiast Chciałbym po prostu wiedzieć, czy watchFestchResults trwa co FETCH_RESULTS. Lub nawet tylko wtedy, gdy odpali takeEvery(). Bez względu na to, jak to nastąpi.

Czy to naprawdę jest sposób na zrobienie tego?

+0

Tak, jestem zaskoczony tym, też. Przykładowe testy porównują wyniki z twórcami efektów sagi. Nie o to mi chodzi. Dbam o to, czy postawią sklep w dobrym stanie. Testowanie za pomocą przykładów wydaje się bardzo kruche i niczego nie dowodzi. –

Odpowiedz

4

Wygląda punktu testowania ich jest osiągnięcie 100% pokrycia testowego.

Są pewne rzeczy, które może przetestować urządzenie, ale wątpliwe jest, czy powinieneś.

Wydaje mi się, że ta sytuacja może być lepszym kandydatem na test „integracja”. Coś, co nie testuje po prostu pojedynczej metody, ale jak kilka metod pracuje razem jako całość. Być może mógłbyś wywołać akcję, która uruchamia reduktor, który używa twojej sagi, a następnie sprawdziłby, czy w sklepie nie ma zmiany? Byłoby to o wiele bardziej znaczące niż testowanie samej sagi.

2

zgadzam się z John Meyer answer że jest lepiej przystosowane do testu integracyjnego niż dla testów jednostkowych. Ten issue jest najpopularniejszy w GitHub na podstawie głosów up. Poleciłbym to przeczytać.

Jedną z sugestii jest użycie redux-saga-tester pakiet utworzony przez otwieracz emisji. Pomaga stworzyć stan początkowy, uruchamia pomocników saga (takeEvery, takeLatest), wysyła akcje, których saga słucha, obserwuje stan, odzyskuje historię działań i nasłuchuje konkretnych działań, które mają się pojawić.

Używam go z axios-mock-adapter, ale istnieje kilka przykładów w kodzie przy użyciu nock.

Saga

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

Przykład testowy używając żartem

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