2016-02-27 146 views
0

我已經在Redux中創建了一個小型學習項目,並基本上從documentation中複製了異步操作的中間件。現在我想爲它編寫一些測試,以確保在我改變一些東西后它能正常工作。在承諾與Mocha,Chai和Sinon完成後測試不承諾

爲了編寫我的測試,我使用了摩卡,chai,chai-as-promised和sinon。

我對redux沒有任何問題,但我不確定如何測試。

從中間件的相關代碼:

export default function callApiMiddleware({ dispatch }) { 
    return next => action => { 
    // ... various checks 

    const [ requestType, successType, errorType ] = types; 
    dispatch({ ...payload, type: requestType }); 

    return callApi().then(
     response => dispatch({ ...payload, type: successType, response }), 
     error => dispatch({ ...payload, type: errorType, error }) 
    ); 
    } 
} 

中間件分派適當行動的承諾是否已兌現或拒絕。

爲了測試這一點,我和其他人一起剔除了調度函數,並且還傳遞了一個虛假承諾,即根據我想要測試的結果完成或拒絕。

我寫了一個測試是這樣的:

it('should trigger success action when successful',() => { 
    let promise = callApiMiddleware({ dispatch, getState })(next)({ 
    ...action, 
    callApi:() => new Promise((resolve, reject) => resolve('SUCCESS')) 
    }); 
    return expect(promise).to.eventually.be.fulfilled; 
}); 

這工作正常,但第一個問題我遇到了,當我試圖模擬一個拒絕承諾,可能會導致從沒有互聯網連接,所以我寫這個測試:

it('should trigger failure action when failure occurs',() => { 
    let promise = callApiMiddleware({ dispatch, getState })(next)({ 
    ...action, 
    callApi:() => new Promise((resolve, reject) => reject('ERROR')) 
    }); 
    return expect(promise).to.eventually.be.rejected; 
}); 

但這種失敗,出現以下消息:

AssertionError: expected promise to be rejected but it was fulfilled with undefined

Expected :[undefined]

Actual :[undefined]

這對我來說沒有任何意義,因爲我明確地傳遞了一個承諾,即只有功能纔會被拒絕。

我的另一個問題是我也想做其他的斷言,這些斷言並不一定與承諾本身有任何關係,但必須在承諾完成後進行評估。我想斷言dispatch方法被調用兩次。在使用sinon的測試中,dispatch方法本身被刪除以執行所需的斷言。可能,我想用這種方式做出多個斷言。

我曾嘗試以下:

it('should trigger success action when successful',() => { 
    let promise = callApiMiddleware({ dispatch, getState })(next)({ 
    ...action, 
    callApi:() => new Promise((resolve, reject) => resolve('SUCCESS')) 
    }); 
    return Q.all([ 
    expect(promise).to.eventually.be.fulfilled, 
    expect(dispatch).to.eventually.be.calledTwice 
    ]); 
}); 

但這返回一些非常大的錯誤,簡單地告訴我,dispatchthenable即不是一個承諾。

我不知道如何做到這一點,所以任何輸入將不勝感激。

+0

雖然這不能回答你的問題,你可能會發現[Redux Saga生成器](http://stackoverflow.com/questions/35654334/how-to-test-api-request-failures-with-redux-saga/35674990#35674990)更容易測試。 –

+0

謝謝!我沒有聽說過,我一定會檢查出來,但是我遇到的問題可能不是特定於redux,但更多的是我試圖找出的測試工具。 – Pavlin

回答

2

你得到滿足undefined無極一的原因是因爲這是中間件的回報:

return callApi().then(
    response => dispatch({ ...payload, type: successType, response }), 
    error => dispatch({ ...payload, type: errorType, error }) 
); 

因爲它不重新拋出的錯誤第二次回調,由此產生的諾言被履行。既然它也沒有返回任何東西,它用undefined來完成。

您可以更改代碼重新拋出的錯誤在這種情況下:

return callApi().then(
    response => dispatch({ ...payload, type: successType, response }), 
    error => { 
    dispatch({ ...payload, type: errorType, error }) 
    throw error; 
    } 
); 

這會給你所期望的結果,但將報告在每一個你的請求失敗時DevTools未處理的拒絕。你的情況可能很好。

至於你的第二個例子:

But this returns some very large error that simply tells me that dispatch is not thenable i.e. not a promise.

它看起來像.to.eventually.*works on Promises。確實,dispatch不是一個承諾,所以你不能像這樣使用它。你可能會想要寫這樣的事情,而不是:

return expect(promise).to.eventually.be.fulfilled.then(() => { 
    expect(dispatch).to.be.calledTwice(); 
}); 

最後,我會鼓勵你檢查出Redux Saga。使用生成器描述副作用比使用自定義中間件更容易,生成器爲way easier to test

+1

感謝您提供非常完整的答案,我對測試工具不是很熟悉,所以這非常有幫助。我絕對會檢查一下這個簡單的事情,它看起來像一個更乾淨的方式,正是我想達到的目標。 – Pavlin

3

Promise不會重新拋出錯誤,所以如果您在第一個捕獲處理程序中發現錯誤,那麼承諾將在下一個處理程序中實現,除非您再次拋出catch錯誤。

// this does not work 
promise.catch((e) => console.log(e)).catch((e) => console.log(e)); 

如果你想這個工作,你必須重新拋出一個錯誤。

promise.catch((e) => { 
    console.log(e); 
    throw e; 
}).catch((e) => console.log(e)); 

如果要通過測試,則需要重新引發承諾的catch處理程序中捕獲的錯誤。所以,你的代碼應該是這樣的:

export default function callApiMiddleware({ dispatch }) { 
    return next => action => { 
    // ... various checks 

    const [ requestType, successType, errorType ] = types; 
    dispatch({ ...payload, type: requestType }); 

    return callApi().then(
     response => dispatch({ ...payload, type: successType, response }), 
     error => { 
      dispatch({ ...payload, type: errorType, error }); 
      throw error; 
     } 
    ); 
    } 
} 
+0

謝謝,我不知道這種行爲。這確實解決了我的第一個問題。 – Pavlin