【发布时间】:2022-01-15 08:09:53
【问题描述】:
在尝试处理更新失败并且我想将按钮重置为其原始状态的情况时,我发现 then(fn1,fn2) 和 then(fn1).catch(fn2) 在被拒绝的承诺后给出了不同的结果,尽管事实上 @987654321 @。
test('Reproduce error', () => {
//setup jsdom
document.body = document.createElement('body');
let button = document.createElement('button');
button.id = 'id';
document.body.appendChild(button);
//setup functions used
let promiseReject;
const updaterMock = function () {
return new Promise((resolve,reject)=>{promiseReject = reject});
};
const markButtonOn = () => {};
const markButtonOff = function () {
console.log("I'm marking off");
button.textContent = 'off';
button.onclick = () => {
button.textContent = 'updating';
console.log("I'm updating");
updaterMock()
//using only then(): test passes
.then(markButtonOn,markButtonOff);
//using both then() and catch(): test fails on last expect()
//.then(markButtonOn).catch(markButtonOff);
};
}
//initial state
markButtonOff();
//always passes
expect(button.textContent).toBe('off');
//try to turn on
button.click();
//always passes
expect(button.textContent).toBe('updating');
//trigger the update to fail
promiseReject();
//force test to wait for the mock's returned promise to resolve
return Promise.resolve().then(() => {
//passes with .then(markButtonOn,markButtonOff)
//fails with .then(markButtonOn).catch(markButtonOff), received 'updating'
expect(button.textContent).toBe('off');
});
});
我尽可能地减少了这种情况,去掉了所有不必要的东西。 (至少,据我所知。我对 Javascript/Typescript 还是很陌生。旁注:我从 here 获得了 promiseReject 技术。)
如果在中间button.onclick 部分的updaterMock() 调用之后更改注释掉的行,则测试结果会更改。
如果我只使用then(fn1,fn2),则测试通过。
如果我使用 then(fn1).catch(fn2) 测试失败,说明 textContent 仍处于“更新”状态。
在这两种情况下,Jest 报告对 console.log() 的所有 3 次调用都失败了:
我要结束了
我正在更新
我要结束了
这告诉我对 markButtonOff 的第二次调用正在执行,但 Jest 没有看到元素的变化。
-
为什么
then(fn1,fn2)和then(fn1).catch(fn2)的行为不同? -
如何更改测试中的内容以解决此问题(理想情况下,只是对测试代码做一些事情,而不是对
markButtonOff生产代码,我直接将其写入测试以方便其他人重现。 )
【问题讨论】:
-
不,
promise.then(fn1, fn2)不一定与promise.then(fn1).catch(fn2)相同 - 如果fn1在第一次调用时抛出错误,fn2不会被调用来对其做出反应 - 它只有在promise拒绝时才会调用。在第二种情况下,如果promise或fn1拒绝链,则将调用fn2。仅仅因为catch(f)等同于.then(undefined, f)并不意味着它与将两者结合起来是一样的。 -
话虽如此,我怀疑问题出在
//force test to wait for the mock's returned promise to resolve- 你不应该期望两个不相关的承诺链以任何特定的顺序得到解决。不同运行之间的顺序可能会一致,但通常无法预测。因此,您不能保证按钮承诺链在测试结束时的承诺链之前完成。 -
@VLAZ
fn1甚至没有被调用,所以我不认为这种情况与这种情况有关,因为这里只发生了拒绝路径。至于结尾部分,这是我根据 Jest 文档中的示例可以得出的最佳解决方案。有什么更好的处理方法? -
保存来自
onclick的promise - 执行p = updaterMock().then(markButtonOn).catch(markButtonOff)然后在测试结束时执行p.then()而不是Promise.resolve().then()- 这将保证断言在promise 链解析后运行.希望正确的时机足以让测试通过。如果不是,至少现在测试是完全可以预测的。 -
@VLAZ 不幸的是,这将涉及尝试从生产代码内部保存结果以用于测试。即使在这个简化的示例中,我也没有看到它像
p那样工作在一个块内,因此在测试结束时无法在该块外访问。
标签: javascript promise jestjs es6-promise