2016-02-26 17 views
22

私はサガがたどることができるすべてのシナリオをテストしようとしていますが、私が望む振る舞いをすることはできません。 これはかなりシンプルですが、私はHTTPリクエスト(ログイン)を持っています。私はAPIメソッドを嘲笑して成功と失敗のケースをテストしたいと思います。Redux SagaでAPIリクエストの失敗をテストするにはどうすればよいですか?

しかし、call effectが私のAPI関数を起動しないように見えますが、実際の動作はまだわかりませんが、ミドルウェアが関数呼び出しを担当していると思います。私のテストで店に行って、私は結果を得ることができません。

私の質問は、非同期呼び出しの隣にあるさまざまなアクション(通常は成功か失敗か)を送信する必要があるときに、どのようにあなたのサガをテストできますか?私は、例えば見

は、私はhere

SAGA.JS

export function* login(action) { 
    try { 
    const user = yield call(api.login, action); 
    return yield put(actions.loginSuccess(user)); 
    } catch(e) { 
    yield put(actions.loginFail(e)); 
    } 
} 

export default function* rootAuthenticationSagas() { 
    yield* takeLatest(LOGIN, login); 
} 

TEST.JSショッピングカートの例では成功してサガを発見し、失敗したが失敗した場合は、テストされることはありません、例えば

describe('login',() => { 
    context('When it fails',() => { 
    before('Stub the api',() => { 
     sinon.stub(api, 'login',() => { 
     // IT NEVER COMES HERE ! 
     return Promise.reject({ error: 'user not found' }); 
     }); 
    }); 

    it('should return a LOGIN_FAIL action',() => { 
     const action = { 
     payload: { 
      name: 'toto', 
      password: '123456' 
     } 
     }; 
     const generator = login(action); 

     // THE CALL YIELD 
     generator.next(); 

     const expectedResult = put({ type: 'LOGIN_FAIL', payload: { error: 'user not found' } }); 
     expect(generator.next().value).to.be.eql(expectedResult); // FAIL BECAUSE I GET A LOGIN_SUCCESS INSTEAD OF A FAIL ONE 
    }); 
    }); 
}); 

答えて

36

Mark’s answerが正しいです。ミドルウェアはこれらの命令を実行します。しかし、これはあなたの人生を楽にします。テストでは、を引数としてをnext()に渡すことができ、生成関数はyieldの結果としてそれを受け取ります。これは、偽のミドルウェアとまったく同じです(実際には、偽の応答を与える代わりにリクエストを実際に起動する点を除いて)。

yieldに任意の値を設定するには、next()に渡します。エラーを「受信」するには、throw()に渡します。あなたの例では:

it('should return a LOGIN_FAIL action',() => { 
    const action = { 
    payload: { 
     name: 'toto', 
     password: '123456' 
    } 
    }; 
    const generator = login(action); 

    // Check that Saga asks to call the API 
    expect(
    generator.next().value 
).to.be.eql(
    call(api.login, action) 
); 

    // Note that *no actual request was made*! 
    // We are just checking that the sequence of effects matches our expectations. 

    // Check that Saga reacts correctly to the failure 
    expect(
    generator.throw({ 
     error: 'user not found' 
    }).value 
).to.be.eql(
    put({ 
     type: 'LOGIN_FAIL', 
     payload: { error: 'user not found' } 
    }) 
); 
}); 
7

正しい - 私はそれを理解し、Reduxの-佐賀の全体のポイントは、あなたのサガ機能が目を記述するオブジェクトを返すために、佐賀のAPIを使用していることですその後、ミドルウェアはそれらのオブジェクトを実際に実行して動作を実行します。したがって、サガのyield call(myApiFunction, "/someEndpoint", arg1, arg2)文は、概念的には{effectType : CALL, function: myApiFunction, params: [arg1, arg2]}のようなオブジェクトを返す可能性があります。

これらの宣言的オブジェクトが実際にどのように見えるかを正確に見て、テストで比較するオブジェクトを作成するか、API関数自体を使用してオブジェクトを作成することができますredux-sagaはテストコードで何をしているのか)。

0

また、このようなredux-saga-testingとして、あなたのサガをテストするためのヘルパーライブラリを使用する場合があります。

免責事項:私は、このライブラリは、あなたのテストを手動でgenerator.next()を呼び出すよりについて推論する多くの方が簡単である任意の他の(同期)テスト、のように見えるようになることを正確に同じ問題を解決するために

をこのライブラリを書きました。

あなたの例をTaking、、あなたがfollowとしてtests write could。

import sagaHelper from 'redux-saga-testing'; 
import { call, put } from 'redux-saga/effects'; 
import actions from './my-actions'; 
import api from './your-api'; 

// Your example 
export function* login(action) { 
    try { 
     const user = yield call(api.login, action); 
     return yield put(actions.loginSuccess(user)); 
    } catch(e) { 
     yield put(actions.loginFail(e.message)); // Just changed that from "e" to "e.message" 
    } 
} 


describe('When testing a Saga that throws an error',() => { 
    const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'})); 

    it('should have called the API first, which will throw an exception', result => { 
     expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'})); 
     return new Error('Something went wrong'); 
    }); 

    it('and then trigger an error action with the error message', result => { 
     expect(result).toEqual(put(actions.loginFail('Something went wrong'))); 
    }); 
}); 

describe('When testing a Saga and it works fine',() => { 
    const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'})); 

    it('should have called the API first, which will return some data', result => { 
     expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'})); 
     return { username: 'Ludo', email: '[email protected]' }; 
    }); 

    it('and then call the success action with the data returned by the API', result => { 
     expect(result).toEqual(put(actions.loginSuccess({ username: 'Ludo', email: '[email protected]' }))); 
    }); 
}); 

More examples((itはusing's Jestのsyntaxを、but itはessentially Mochaとsame's、itはcompletelyテストライブラリ - agnostic's) Jest、MochaおよびAVAを使用して)GitHubにある。

関連する問題