簡體   English   中英

如何用笑話測試redux saga?

[英]How to test redux saga with jest?

react,react-redux / saga和jest中的新功能

考慮:

-----組分()----

componentDidMount() {

    this.props.actions.initTodos(
        axios,
        ajaxURLConstants.WP_GET_TODOS,
        appStateActions.setAppInIdle,
        appStateActions.setAppInProcessing,
        todosActions.todosInitialized
    );

}

因此,當我的TodoApp組件掛載時,它將分派INIT_TODOS操作,然后我的根傳奇正在偵聽事件 ,當它被捕獲時,將產生適當的worker傳奇以采取相應的行動。

-----相應的工人傳奇-----

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

在check put(action)時未捕獲:參數action未定義

但是我有console.log我在worker saga中通過測試的動作,並且確實不是不確定的

我想念什么?

提前致謝。

----------更新------------

好的,請注意,在上面這行代碼中有抱怨

yield put( action.setAppInIdle() );

這是在try catch塊之外,因此我進行了一些更改

1.)我將代碼移到try catch塊內的上方,就在

if ( response.data.status === "success" )

請檢查上面的initTodosSaga代碼

然后在我的傳奇測試中,我測試

expect( action.setAppInProcessing ).toHaveBeenCalled();

代替setAppInIdle間諜函數

這是測試結果

在此處輸入圖片說明

所以測試通過了! 但它仍然抱怨動作未定義

現在有趣的是,如果在我的傳奇測試中,如果我現在對此進行測試

expect( action.setAppInProcessing ).toHaveBeenCalled();
expect( action.setAppInIdle ).toHaveBeenCalled();

這是結果

在此處輸入圖片說明

所以現在它仍然抱怨該操作仍未定義(我的屏幕截圖中未包含該操作,但與上面相同)

再加上第二個斷言,我沒有調用setAppInIdle間諜函數,但是setAppInProcessing確實通過了!

我希望此附加信息有助於解決此問題。

沒有外部庫的幫助,似乎很難測試redux saga

對我來說我使用了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
    );

}

因此,當該組件安裝好后,它會觸發我的根傳奇偵聽並捕獲的動作,然后生成適當的worker傳奇以采取相應行動

再次注意,我傳遞了所有必要的數據,這些信息需要工人傳奇才能在動作有效負載上正常運行。

----- initTodoSaga(工人傳奇)-----

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

現在應該失敗

在此處輸入圖片說明

因此它輸出的期望值未得到滿足 ,因為我們預期要執行的操作沒有

--------------------測試2 --------------------

現在,此測試是一個傳奇,它導入了需要操作的某些東西,這與我的第一個測試不同,我在動作有效負載中輸入了axios,動作創建者

這個傳奇故事導入了axios,它需要操作的動作創建者

值得慶幸的是, Redux Saga測試計划具有一些輔助功能,可虛假數據“饋送”到Saga

我將只跳過觸發根傳奇正在監聽的動作的組件,這並不重要,我將直接粘貼傳奇和saga測試

---- 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函數允許您模擬函數調用,同時提供應返回的偽數據

就是這樣,我現在可以測試我的sagas! 好極了!

還有一件事,當我為自己的傳奇運行測試時,導致執行帶有警報代碼的代碼

例如

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

一些注意事項:

  • 您實際上不必讓模擬Axios.get返回任何內容
  • 使用expect語句,我正在將生成器的產量與期望生成器執行的操作進行比較(即執行putcall語句)
  • 模擬響應中缺少data屬性

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM