React、react-redux/sagaおよびjestの新機能
考慮してください:
-----コンポーネント()----
componentDidMount() {
this.props.actions.initTodos(
axios,
ajaxURLConstants.WP_GET_TODOS,
appStateActions.setAppInIdle,
appStateActions.setAppInProcessing,
todosActions.todosInitialized
);
}
したがって、私のTodoAppコンポーネントがマウントされたとき、INIT_TODOSアクションをディスパッチしますその後、私のルートサガはリッスンし、それをキャッチすると、それに応じて行動する適切なワーカーサガを生成します。
-----対応する労働者佐賀-----
export function* initTodosSaga( action ) {
try {
yield put( action.setAppInProcessing() );
let response = yield call( action.axios.get , action.WP_GET_TODOS );
if ( response.data.status === "success" )
yield put( action.todosInitialized( response.data.todos ) );
else {
console.log( response );
alert( response.data.error_msg );
}
} catch ( error ) {
console.log( "error" , error );
alert( "Failed to load initial data" );
}
yield put( action.setAppInIdle() );
}
-----これまでのテスト-----
import todos from "../../__fixtures__/todos";
import { initTodosSaga } from "../todosSaga";
test( "saga test" , () => {
let response = {
status : "success",
todos
},
action = {
axios : {
get : function() {
return new Promise( ( resolve , reject ) => {
resolve( response );
} );
}
},
WP_GET_TODOS : "dummy url",
setAppInIdle : jest.fn(),
setAppInProcessing : jest.fn(),
todosInitialized : jest.fn()
};
let initTodosSagaGen = initTodosSaga( action );
initTodosSagaGen.next();
expect( action.setAppInIdle ).toHaveBeenCalled();
} );
-----テスト結果-----
重要な部分はこれです
console.error node_modules\redux-saga\lib\internal\utils.js:240
チェックput(action)でキャッチされない:引数actionは未定義です
しかし、私はconsole.logにワーカーサガ内のテストで渡したアクションを持っていますが、実際には未定義ではありません
私は何が欠けていますか?
前もって感謝します。
----------更新------------
OK、このコード行で文句を言っていることに注意してください
yield put( action.setAppInIdle() );
Try catchブロックの外側にあるため、いくつかの変更を加えました
1.)上記のコードをtry catchブロック内で、elseステートメントの直後に移動しました
if ( response.data.status === "success" )
上記のコードinitTodosSagaを確認してください
それから私のサガテストで、私はテストします
expect( action.setAppInProcessing ).toHaveBeenCalled();
setAppInIdlespy関数の代わりに
これがテスト結果です
テストに合格しました! しかし、それでもアクションが未定義であることに文句を言っています
面白いのは、私のサガテストで、今これをテストするかどうかです
expect( action.setAppInProcessing ).toHaveBeenCalled();
expect( action.setAppInIdle ).toHaveBeenCalled();
これが結果です
そのため、まだ定義されていないアクションについて不平を言っています(スクリーンショットには含まれていませんが、上記と同じです)
プラス2番目のアサートについてsetAppInIdlespy関数は呼び出されませんでしたが、setAppInProcessing合格しました!
この追加情報がこの質問の解決に役立つことを願っています。
外部ライブラリの助けなしにreduxの物語をテストすることは非常に難しいようです
私は https://github.com/jfairbank/redux-saga-test-plan を使用しました
このライブラリは非常に優れています。
ここに私のテストがあります
--------------------テスト1 ------------------- -
そのため、このテストでは、サガが機能するために必要なほぼすべてのアクションペイロードを渡しました。 axios、アクションクリエーター関数など...依存性注入の原理に従うので、テストが簡単です。
----- TodoAppコンポーネント-----
componentDidMount() {
this.props.actions.initTodos(
axios,
ajaxURLConstants.WP_GET_TODOS,
appStateActions.setAppInIdle,
appStateActions.setAppInProcessing,
todosActions.todosInitialized,
todosActions.todosFailedInit
);
}
そのため、コンポーネントがマウントされたときに、ルートサガがリッスンしてキャッチするアクションを起動し、適切なワーカーサガを生成して、それに応じて動作します
ここでも、ワーカーサーガがアクションペイロードで適切に操作するために必要なすべてのデータを渡すことに注意してください。
----- initTodoSaga(Worker Saga)-----
export function* initTodosSaga( action ) {
try {
yield put( action.setAppInProcessing() );
let response = yield call( action.axios.get , action.WP_GET_TODOS );
if ( response.data.status === "success" )
yield put( action.todosInitialized( response.data.todos ) );
else {
console.log( response );
alert( response.data.error_msg );
yield put( action.todosFailedInit( response ) );
}
} catch ( error ) {
console.log( "error" , error );
alert( "Failed to load initial data" );
yield put( action.todosFailedInit( error ) );
}
yield put( action.setAppInIdle() );
}
-----佐賀テスト-----
import { expectSaga } from "redux-saga-test-plan";
import { initTodosSaga } from "../todosSaga";
test( "should initialize the ToDos state via the initTodoSaga" , () => {
let response = {
data : {
status : "success",
todos
}
},
action = {
axios : {
get : function() {
return new Promise( ( resolve , reject ) => {
resolve( response );
} );
}
},
WP_GET_TODOS : "dummy url",
setAppInIdle : appStateActions.setAppInIdle,
setAppInProcessing : appStateActions.setAppInProcessing,
todosInitialized : todosStateActions.todosInitialized,
todosFailedInit : todosStateActions.todosFailedInit
};
// This is the important bit
// These are the assertions
// Basically saying that the actions below inside the put should be dispatched when this saga is executed
return expectSaga( initTodosSaga , action )
.put( appStateActions.setAppInProcessing() )
.put( todosStateActions.todosInitialized( todos ) )
.put( appStateActions.setAppInIdle() )
.run();
} );
そして、私のテストパスイェーイ! :)テストが失敗したときにエラーメッセージを表示するために、initTodosSagaでこのコード行をコメントアウトします
yield put( action.setAppInIdle() );
だから今、アサーション
.put( appStateActions.setAppInIdle() )
今失敗するはずです
そのため、put expectation unmetを出力します。
--------------------テスト2 ------------------- -
このテストは、アクションペイロード内のアクションクリエーターであるaxiosにフィードする最初のテストとは異なり、操作に必要なものをインポートするサガ用です。
このサガはaxiosをインポートし、操作する必要があるアクションクリエーター
ありがたいことにRedux Sagaテスト計画には "feed"ダミーへのヘルパー関数があります佐賀へのデータ
ルートサガがリッスンしているアクションを実行するコンポーネントをスキップします。重要ではありません。サガとサガテストを直接貼り付けます。
---- addTodoSaga ----
/** global ajaxurl */
import axios from "axios";
import { call , put } from "redux-saga/effects";
import * as appStateActions from "../actions/appStateActions";
import * as todosActions from "../actions/todosActions";
export function* addTodoSaga( action ) {
try {
yield put( appStateActions.setAppInProcessing() );
let formData = new FormData;
formData.append( "todo" , JSON.stringify( action.todo ) );
let response = yield call( axios.post , ajaxurl + "?action=wptd_add_todo" , formData );
if ( response.data.status === "success" ) {
yield put( todosActions.todoAdded( action.todo ) );
action.successCallback();
} else {
console.log( response );
alert( response.data.error_msg );
}
} catch ( error ) {
console.log( error );
alert( "Failed to add new todo" );
}
yield put( appStateActions.setAppInIdle() );
}
-----テスト-----
import axios from "axios";
import { expectSaga } from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import * as appStateActions from "../../actions/appStateActions";
import * as todosStateActions from "../../actions/todosActions";
import { addTodoSaga } from "../todosSaga";
test( "should dispatch TODO_ADDED action when adding new todo is successful" , () => {
let response = {
data : { status : "success" }
},
todo = {
id : 1,
completed : false,
title : "Browse 9gag tonight"
},
action = {
todo,
successCallback : jest.fn()
};
// Here are the assertions
return expectSaga( addTodoSaga , action )
.provide( [
[ matchers.call.fn( axios.post ) , response ]
] )
.put( appStateActions.setAppInProcessing() )
.put( todosStateActions.todoAdded( todo ) )
.put( appStateActions.setAppInIdle() )
.run();
} );
したがって、provide関数を使用すると、関数呼び出しをモックすると同時に、返す必要があるダミーデータを提供できます。
それだけです、今、私のサガをテストすることができます!わーい!
もう1つ、サガに対してテストを実行すると、アラートコード付きのコードが実行されます
例.
alert( "Earth is not flat!" );
コンソールでこれを入手しました
Error: Not implemented: window.alert
そして、その下にたくさんのスタックトレースがあります。ノードにアラートオブジェクトが存在しないためでしょうか?これをどうやって隠すの?回答があればコメントを追加してください。
これが誰にも役立つことを願っています
テストの作業バージョンは次のとおりです。
import todos from '../../__fixtures__/todos';
import { initTodosSaga } from '../todosSaga';
import { put, call } from 'redux-saga/effects';
test('saga test', () => {
const response = {
data: {
status: 'success',
todos
}
};
const action = {
axios: {
get() {}
},
WP_GET_TODOS: 'dummy url',
setAppInIdle: jest.fn().mockReturnValue({ type: 'setAppInIdle' }),
setAppInProcessing: jest.fn().mockReturnValue({ type: 'setAppInProcessing' }),
todosInitialized: jest.fn().mockReturnValue({ type: 'todosInitialized' })
};
let result;
const initTodosSagaGen = initTodosSaga(action);
result = initTodosSagaGen.next();
expect(result.value).toEqual(put(action.setAppInProcessing()));
expect(action.setAppInProcessing).toHaveBeenCalled();
result = initTodosSagaGen.next();
expect(result.value).toEqual(call(action.axios.get, action.WP_GET_TODOS));
result = initTodosSagaGen.next(response);
expect(action.todosInitialized).toHaveBeenCalled();
expect(result.value).toEqual(put(action.todosInitialized(response.data.todos)));
result = initTodosSagaGen.next();
expect(action.setAppInIdle).toHaveBeenCalled();
expect(result.value).toEqual(put(action.setAppInIdle()));
});
いくつかのメモ:
expect
ステートメントを使用して、ジェネレーターの歩留まりをジェネレーターに期待するものと比較しています(つまり、put
およびcall
ステートメントを実行します)data
プロパティがモック応答にありませんでした