【问题标题】:Testing non-promises after promise completes with Mocha, Chai and Sinon用 Mocha、Chai 和 Sinon 在 promise 完成后测试 non-promises
【发布时间】:2016-06-11 02:00:47
【问题描述】:

我在 Redux 中创建了一个小型学习项目,并且基本上从 documentation 复制了用于异步操作的中间件。现在我想为它写一些测试,以确保它在我改变一些东西后能正常工作。

为了编写我的测试,我使用的是 Mocha,带有 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: 预期的承诺被拒绝,但它被 undefined 满足

预期:[未定义]

实际:[未定义]

这对我来说毫无意义,因为我清楚地传递了一个唯一的功能就是被拒绝的承诺。

我的另一个问题是我还想做出其他断言,这些断言不一定与承诺本身有任何关系,但必须在承诺完成时进行评估,例如我想断言dispatch 方法被调用了两次。 dispatch 方法本身在使用 sinon 的测试中被存根,以执行所需的断言。潜在地,我想以这种方式做出多个断言。

我尝试了以下方法:

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

但这会返回一些非常大的错误,它只是告诉我dispatch 不是thenable,即不是一个承诺。

我不知道如何做到这一点,所以任何意见都将不胜感激。

【问题讨论】:

  • 虽然这不能回答您的问题,但您可能会发现 Redux Saga generators 更容易测试。
  • 谢谢!我没听说过,我一定会去看看,但我遇到的问题可能并不特定于 redux,而更多的是我试图弄清楚的测试工具。

标签: javascript node.js unit-testing mocha.js redux


【解决方案1】:

Promise 不会重新抛出错误,因此如果您在第一个 catch 处理程序中捕获错误,除非您再次抛出捕获的错误,否则 Promise 将在下一个处理程序中实现。

// 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));

如果你想通过测试,你需要重新抛出在一个 Promise 的 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;
      }
    );
  }
}

【讨论】:

  • 谢谢,我不知道这种行为。这确实解决了我的第一个问题。
【解决方案2】:

你得到一个用 undefined 实现的 Promise 的原因是因为这是中间件返回的:

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

由于它没有在第二个回调中重新抛出错误,因此结果 Promise 已实现。因为它也不返回任何东西,所以它用undefined来实现。

在这种情况下,您可以更改代码以重新抛出错误:

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

这将给出您期望的结果,但每次您的请求失败时都会在 DevTools 中报告未处理的拒绝。在您的情况下可能没问题。

至于你的第二个例子:

但是这会返回一些非常大的错误,它只是告诉我 dispatch is not thenable 即不是一个承诺。

看起来像 .to.eventually.* works on Promises。确实,dispatch 不是 Promise,所以你不能这样使用它。你可能想写这样的东西:

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

最后,我建议您查看Redux Saga。使用生成器描述副作用比使用自定义中间件更容易,生成器是way easier to test

【讨论】:

  • 感谢您提供非常完整的答案,我对测试工具不是很熟悉,所以这很有帮助。我肯定会检查 redux-saga,它看起来是一种更简洁的方式来完成我想要实现的目标。
猜你喜欢
  • 2015-12-01
  • 1970-01-01
  • 2019-02-04
  • 2018-04-08
  • 2016-05-26
  • 2023-04-11
  • 2017-09-14
  • 2020-10-11
  • 1970-01-01
相关资源
最近更新 更多