10

我有两个文件,getItemInfo.js用于进行 API 调用,getItemInfo.test.js分别是 Jest 测试文件。

在测试文件中,我正在模拟 node 模块触发的 http 调用request-promise

*********问题在第二个代码块上,由's包围。基本上为什么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

我究竟做错了什么?

4

2 回答 2

12

为什么我的 jest.mock 中的 Promise reject() 会转到 then() 而不是 catch()?

您的.catch()处理程序正在将拒绝的承诺转换为已解决的承诺,因此只.then()调用外部处理程序。

当你.catch()这样使用时:

.catch((err) => {
    return err;
});

并且不要重新抛出错误或返回被拒绝的承诺,那么拒绝被认为是“已处理”并且返回的承诺被解决,而不是被拒绝。这就像使用 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
});
于 2017-08-08T06:08:51.030 回答
2

因为您已将拒绝转换为解决方案:

.catch((err) => {
    return err;
});

如果您希望拒绝传播到 之外getItemInfo,请从 中删除catch处理程序getItemInfo

请记住,catchcreatethen和 return Promise 都被视为相同的处理程序:

  1. 如果您从他们那里返回一个值,则承诺then/ catchcreated 将通过该值实现。

  2. 如果你从他们那里返回一个thenable,promise then/ catchcreated 将被解析为那个 thenable(根据 thenable 的作用实现或拒绝)。

  3. 如果你它们扔进去,promise then/ catchcreated 就会被这个错误拒绝。

(FWIW,我在我博客上的这篇文章中谈到了承诺术语——“履行”与“解决”等。)

你只需要一个catch处理程序,如果:

  • 您没有将链条传递给其他东西(您在其中getItemInfo),或者

  • 您需要以某种方式转换错误,要么将其转换为解决方案(恢复),要么将其转换为不同的错误。要执行后者,您throw或返回一个被拒绝或将被拒绝的承诺。

于 2017-08-08T06:08:56.640 回答