为什么我的 jest.mock 中的 Promise reject() 会转到 then() 而不是 catch()?
Why is the Promise reject() in my jest.mock going to a then() rather than a catch()?
我有两个文件,getItemInfo.js
进行 API 调用,getItemInfo.test.js
是相应的 Jest 测试文件。
在测试文件中,我正在模拟由节点模块request-promise
触发的http调用。
问题在第二个代码块上,被*********
包围。
基本上为什么 reject()
错误在第二个单元测试中仍然会进入 then()
块?
// getItemInfo.js
const rp = require('request-promise');
const getItemInfo = (id) => {
const root = 'https://jsonplaceholder.typicode.com/posts/';
const requestOptions = {
uri: `${root}/${id}`,
method: 'GET',
json: true
}
return rp(requestOptions)
.then((result) => {
return result;
})
.catch((err) => {
return err;
});
};
module.exports = {
getItemInfo: getItemInfo
};
这是我的 Jest 单元测试文件。
// getItemInfo.test.js
const ItemService = require('./getItemInfo');
jest.mock('request-promise', () => (options) => {
const id = Number.parseInt(options.uri.substring(options.uri.lastIndexOf('/') + 1));
return new Promise((resolve, reject) => {
if (id === 12) {
return resolve({
id: id,
userId: 1,
title: '',
body: ''
});
} else {
return reject('something went wrong'); // <-- HERE IS THE REJECT
}
})
});
describe('getItemInfo', () => {
it('can pass', done => {
const TEST_ID = 12
ItemService.getItemInfo(TEST_ID).then((result) => {
console.log('result:',result);
expect(result.id).toBe(TEST_ID);
expect(result.userId).toBeDefined();
expect(result.title).toBeDefined();
expect(result.body).toBeDefined();
done();
});
});
it('can fail', (done) => {
const TEST_ID = 13;
ItemService.getItemInfo(TEST_ID)
.catch((err) => {
// *************
// This "catch" block never runs
// even if the jest.mock above Promise.rejects
// Why is that???
// *************
console.log('catch():', err);
done();
})
.then((result) => {
// this block runs instead.
// and it returns "then: something went wrong"
console.log('then():', result);
done();
});
});
});
这是单元测试的输出。该命令只是 jest
。最后一行应该是 catch()
语句中的 运行,而不是 then()
:
PASS ./getItemInfo.test.js
getItemInfo
✓ can pass (9ms)
✓ can fail (1ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.703s, estimated 1s
Ran all test suites.
----------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
getItemInfo.js | 100 | 100 | 100 | 100 | |
----------------|----------|----------|----------|----------|----------------|
console.log getItemInfo.test.js:25
result: { id: 12, userId: 1, title: '', body: '' }
console.log getItemInfo.test.js:48
then(): something went wrong
我做错了什么?
Why is the Promise reject() in my jest.mock going to a then() rather than a catch()?
您的 .catch()
处理程序正在将拒绝的承诺转换为已解决的承诺,因此仅调用外部 .then()
处理程序。
当你像这样使用.catch()
时:
.catch((err) => {
return err;
});
并且不重新抛出错误或 return 被拒绝的承诺,然后拒绝被视为 "handled" 并且 returned 承诺被解决,而不是被拒绝。这就像使用 try/catch 一样。在 catch 处理程序中,异常会被处理,除非您再次抛出它。
您可以在这个简单的片段中看到:
new Promise((resolve, reject) => {
reject(new Error("reject 1"));
}).catch(err => {
// returning a normal value here (anything other than a rejected promise)
// causes the promise chain to flip to resolved
return err;
}).then(val => {
console.log("Promise is now resolved, not rejected");
}).catch(err => {
console.log("Don't get here");
});
真的没有理由:
.then((result) => {
return result;
})
.catch((err) => {
return err;
});
您可以将它们都删除。 .then()
处理程序只是多余的代码,.catch()
处理程序吃掉拒绝并将其变成已解决的承诺。
如果您想保留 .catch()
处理程序,但允许拒绝向上传播,那么您需要重新抛出。
.catch((err) => {
console.log(err);
throw err; // keep promise rejected so reject will propagate upwards
});
因为您已将拒绝转化为决议:
.catch((err) => {
return err;
});
如果您希望拒绝从 getItemInfo
传播出去,请从 getItemInfo
中删除 catch
处理程序。
请记住 catch
和 then
都创建和 return 承诺,并且它们的处理程序被相同对待:
如果你 return 从他们那里得到一个值,那么 then
/catch
创建的承诺就实现了值。
如果你return一个thenable他们的承诺then
/ catch
已创建 解决为 那个 thenable(完成或拒绝基于那个 thenable 的行为)。
如果您在其中 throw,则创建的承诺 then
/catch
会因该错误而被拒绝。
(FWIW,我在我的博客 this post 中介绍了承诺术语——“实现”与“解决”等。)
您只需要一个 catch
处理程序,如果:
您没有将链条传递给其他东西(您在 getItemInfo
中),或者
您需要以某种方式转换错误,将其转换为解决方案(恢复)或将其转换为不同的错误。要执行后者,您 throw
或 return 一个已被拒绝或将被拒绝的承诺。
我有两个文件,getItemInfo.js
进行 API 调用,getItemInfo.test.js
是相应的 Jest 测试文件。
在测试文件中,我正在模拟由节点模块request-promise
触发的http调用。
问题在第二个代码块上,被*********
包围。
基本上为什么 reject()
错误在第二个单元测试中仍然会进入 then()
块?
// getItemInfo.js
const rp = require('request-promise');
const getItemInfo = (id) => {
const root = 'https://jsonplaceholder.typicode.com/posts/';
const requestOptions = {
uri: `${root}/${id}`,
method: 'GET',
json: true
}
return rp(requestOptions)
.then((result) => {
return result;
})
.catch((err) => {
return err;
});
};
module.exports = {
getItemInfo: getItemInfo
};
这是我的 Jest 单元测试文件。
// getItemInfo.test.js
const ItemService = require('./getItemInfo');
jest.mock('request-promise', () => (options) => {
const id = Number.parseInt(options.uri.substring(options.uri.lastIndexOf('/') + 1));
return new Promise((resolve, reject) => {
if (id === 12) {
return resolve({
id: id,
userId: 1,
title: '',
body: ''
});
} else {
return reject('something went wrong'); // <-- HERE IS THE REJECT
}
})
});
describe('getItemInfo', () => {
it('can pass', done => {
const TEST_ID = 12
ItemService.getItemInfo(TEST_ID).then((result) => {
console.log('result:',result);
expect(result.id).toBe(TEST_ID);
expect(result.userId).toBeDefined();
expect(result.title).toBeDefined();
expect(result.body).toBeDefined();
done();
});
});
it('can fail', (done) => {
const TEST_ID = 13;
ItemService.getItemInfo(TEST_ID)
.catch((err) => {
// *************
// This "catch" block never runs
// even if the jest.mock above Promise.rejects
// Why is that???
// *************
console.log('catch():', err);
done();
})
.then((result) => {
// this block runs instead.
// and it returns "then: something went wrong"
console.log('then():', result);
done();
});
});
});
这是单元测试的输出。该命令只是 jest
。最后一行应该是 catch()
语句中的 运行,而不是 then()
:
PASS ./getItemInfo.test.js
getItemInfo
✓ can pass (9ms)
✓ can fail (1ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.703s, estimated 1s
Ran all test suites.
----------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
getItemInfo.js | 100 | 100 | 100 | 100 | |
----------------|----------|----------|----------|----------|----------------|
console.log getItemInfo.test.js:25
result: { id: 12, userId: 1, title: '', body: '' }
console.log getItemInfo.test.js:48
then(): something went wrong
我做错了什么?
Why is the Promise reject() in my jest.mock going to a then() rather than a catch()?
您的 .catch()
处理程序正在将拒绝的承诺转换为已解决的承诺,因此仅调用外部 .then()
处理程序。
当你像这样使用.catch()
时:
.catch((err) => {
return err;
});
并且不重新抛出错误或 return 被拒绝的承诺,然后拒绝被视为 "handled" 并且 returned 承诺被解决,而不是被拒绝。这就像使用 try/catch 一样。在 catch 处理程序中,异常会被处理,除非您再次抛出它。
您可以在这个简单的片段中看到:
new Promise((resolve, reject) => {
reject(new Error("reject 1"));
}).catch(err => {
// returning a normal value here (anything other than a rejected promise)
// causes the promise chain to flip to resolved
return err;
}).then(val => {
console.log("Promise is now resolved, not rejected");
}).catch(err => {
console.log("Don't get here");
});
真的没有理由:
.then((result) => {
return result;
})
.catch((err) => {
return err;
});
您可以将它们都删除。 .then()
处理程序只是多余的代码,.catch()
处理程序吃掉拒绝并将其变成已解决的承诺。
如果您想保留 .catch()
处理程序,但允许拒绝向上传播,那么您需要重新抛出。
.catch((err) => {
console.log(err);
throw err; // keep promise rejected so reject will propagate upwards
});
因为您已将拒绝转化为决议:
.catch((err) => {
return err;
});
如果您希望拒绝从 getItemInfo
传播出去,请从 getItemInfo
中删除 catch
处理程序。
请记住 catch
和 then
都创建和 return 承诺,并且它们的处理程序被相同对待:
如果你 return 从他们那里得到一个值,那么
then
/catch
创建的承诺就实现了值。如果你return一个thenable他们的承诺
then
/catch
已创建 解决为 那个 thenable(完成或拒绝基于那个 thenable 的行为)。如果您在其中 throw,则创建的承诺
then
/catch
会因该错误而被拒绝。
(FWIW,我在我的博客 this post 中介绍了承诺术语——“实现”与“解决”等。)
您只需要一个 catch
处理程序,如果:
您没有将链条传递给其他东西(您在
getItemInfo
中),或者您需要以某种方式转换错误,将其转换为解决方案(恢复)或将其转换为不同的错误。要执行后者,您
throw
或 return 一个已被拒绝或将被拒绝的承诺。