2016-02-27 149 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