Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/383.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在Mocha、Chai和Sinon完成承诺后测试非承诺_Javascript_Node.js_Unit Testing_Mocha.js_Redux - Fatal编程技术网

Javascript 在Mocha、Chai和Sinon完成承诺后测试非承诺

Javascript 在Mocha、Chai和Sinon完成承诺后测试非承诺,javascript,node.js,unit-testing,mocha.js,redux,Javascript,Node.js,Unit Testing,Mocha.js,Redux,我已经在Redux中创建了一个小的学习项目,并且从本质上复制了用于异步操作的中间件。现在我想为它写一些测试,以确保在我改变一些事情后它能正常工作 为了编写我的测试,我使用了摩卡,chai,chai和sinon 我对redux没有任何问题,但我不确定如何测试 中间件中的相关代码: export default function callApiMiddleware({ dispatch }) { return next => action => { // ... variou

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

为了编写我的测试,我使用了摩卡,chai,chai和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;
});
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;
      }
    );
  }
}
这很好,但我遇到的第一个问题是,当我试图模拟一个可能因没有互联网连接而被拒绝的承诺时,我写了这个测试:

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:预期承诺将被拒绝,但未定义的

预期:[未定义]

实际:[未定义]

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

我的另一个问题是,我还想做出其他断言,这些断言不一定与承诺本身有任何关系,但必须在承诺完成时进行评估,例如,我想断言
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
不是
,也就是说,不是承诺


我不知道该怎么做,所以任何输入都将不胜感激。

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

// 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));
若希望测试通过,则需要重新显示在承诺的捕获处理程序中捕获的错误。因此,您的代码应该如下所示:

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

承诺不会重新抛出错误,所以如果您在第一个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));
若希望测试通过,则需要重新显示在承诺的捕获处理程序中捕获的错误。因此,您的代码应该如下所示:

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

使用
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中报告未处理的拒绝。这对你来说可能没问题

至于你的第二个例子:

但这返回了一个非常大的错误,它简单地告诉我调度是不可能的,即不是一个承诺

它看起来像是
.to.finally.
。实际上,
dispatch
并不是一个承诺,所以你不能这样使用它。您可能想写这样的内容:

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

最后,我鼓励你退房。使用生成器描述副作用可能比使用自定义中间件更容易,而且生成器确实如此。

使用未定义的
实现承诺的原因是因为中间件会返回以下内容:

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中报告未处理的拒绝。这对你来说可能没问题

至于你的第二个例子:

但这返回了一个非常大的错误,它简单地告诉我调度是不可能的,即不是一个承诺

它看起来像是
.to.finally.
。实际上,
dispatch
并不是一个承诺,所以你不能这样使用它。您可能想写这样的内容:

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

最后,我鼓励你退房。使用生成器描述副作用可能比使用自定义中间件更容易,而且生成器确实如此。

虽然这并不能回答您的问题,但您可能会发现测试起来要容易得多。谢谢!我没有听说过,我一定会去看看,但我遇到的问题可能不是特定于redux的,而是我正在尝试解决的测试工具。虽然这并不能回答您的问题,但您可能会发现测试更容易。谢谢!我没有听说过,我肯定会去看看,但我遇到的问题可能不是特定于redux,而是我正在尝试解决的测试工具。谢谢,我没有意识到这种行为。这确实解决了我的第一个问题。谢谢你,我没有意识到这种行为。这确实解决了我的第一个问题。谢谢你提供了一个非常完整的答案,我对测试工具不是很熟悉,所以这非常有帮助。我一定会去看看redux传奇,它看起来是一种更干净的方法,完全符合我的目标。谢谢你提供了一个非常完整的答案,我对测试工具不是很熟悉,所以这非常有帮助。我一定会看看redux传奇,它看起来是一种更干净的方式,完全符合我的目标。