无法存根函数返回承诺

Cannot Stub Function Returning Promise

我试图存根箭头函数 removeUserEntry,但是在测试中执行 acctRmRouter 时,我的存根似乎被忽略了。我必须明确地存根 UserModeldeleteOne 方法才能成功获得测试,我想知道为什么会发生无知,谢谢

acctRoute.js

const removeUserEntry = (username) => {
    const condition = {username: username};

    return UserModel.deleteOne(condition)
       .then((res) => {
           if (res.n < 1) {
               throw new Error('User not exists');
           }
           return true;
       }, (err) => {throw err})
       .catch(err => err);
};

const acctRmRouter = function(httpReq, httpRes, next) {
    if (!argValidate(httpReq.body, 'string')) {
        httpRes.locals = {api: { success: false }};
        // return to avoid running downwards
        return next(new Error('bad argument'));
    }

    // perform DB rm user
    return removeUserEntry(httpReq.body).then((res) => {
        if (res === true) {
            httpRes.locals = {api: { success: true }};
            next();
        } else {
            httpRes.locals = {api: { success: false }}
            next(res);
        }
    });
};

acctRoute.spec.js

it('should remove user handler pass success request', async () => {
    shouldDbReset = false;
    const mockRequestURL = "/api/account/rm-user";
    const mockRequest = httpMocks.createRequest({
        method: "POST",
        url: mockRequestURL,
        headers: {
            "Content-Type": "text/plain"
        },
        body: 'validRmUser',
    });
    const mockResponse = httpMocks.createResponse();
    const spyNext = sinon.spy();
    const stubRemoveUserEntry = sinon.stub(accountRouterHelper, 'removeUserEntry'); 

    stubRemoveUserEntry.callsFake(function(){
        return Promise.resolve(true);
    }); // Expecting this function to be stubbed, and always return true

    await accountRouterHelper.acctRmRouter(mockRequest, mockResponse, spyNext); 
    /* But when running the function, it returns error object with "User not exists" 
    which is not what intended */

    const firstCallArgs = spyNext.getCall(0).args[0];

    expect(spyNext.called).to.be.true;
    console.log(`firstCallArgs: ${firstCallArgs}`)
    expect(firstCallArgs instanceof Error).to.be.false;
    expect(spyNext.args[0].length).to.equal(0);
    expect(mockResponse.statusCode).to.equal(200);
    expect(mockResponse.locals.api.success).to.be.true;

    stubRemoveUserEntry.resetHistory();
    stubRemoveUserEntry.restore();
});

以下确实以与 removeUserEntry.

类似的模式成功存根

acctRoute.js

const createUserEntry = (userData) => {
    const updatedUserData = filterInput(userData);
    const userDoc = new UserModel(updatedUserData);
    return userDoc.save()
    .then((userObj) => userObj._doc
    ,(err) => { throw err;})
    .catch(err => err);
};

const acctCreateRouter = function (httpReq, httpRes, next) {
// do something in mongodb
return createUserEntry(userCondition)
   .then((response) => {
            if (!(response instanceof Error)) {
                httpRes.locals = {api: { success: true}};
                next();
            } else {
                httpRes.locals = {api: { success: false}};
                next(response);
            }
        }, (err) => {
            httpRes.locals = {api: { success: false}};
            next(err);
        })
        .catch((err) => {
            httpRes.locals = {api: { success: false}};
            next(err);
        });     
};

const acctOutputRouter = function(req, res, next) {
    if (res.locals) {
        res.send(res.locals.api);
    } else {next()}
};

acctRoute.spec.js

it("should return and save the success result to response locals for next route", () => {
        shouldDbReset = false;
        const mockResponse = httpMocks.createResponse();
        const stubCreateUserEntry = sinon.stub(accountRouterHelper, 'createUserEntry');
        const mockNext = sinon.spy();

        stubCreateUserEntry.callsFake(function(){
            return Promise.resolve();
        }); // Unlike removeUserEntry, stubbing neatly with desired output
        return accountRouterHelper.acctCreateRouter(mockRequest, mockResponse, mockNext)
        .then(() => {
            expect(mockNext.called).to.be.true;
            expect(mockResponse.locals.api.success).to.be.true;
        })
        .finally(() => {
            mockNext.resetHistory();
            stubCreateUserEntry.restore();
        });
    });

问题

sinon.stub(accountRouterHelper, 'removeUserEntry') 替换模块导出。

acctRmRouter() 不是在调用模块导出,它是直接调用 removeUserEntry() 所以存根模块导出什么都不做。

解决方案

重构 acctRmRouter() 以调用 removeUserEntry() 的模块导出。

ES6

// import module into itself
import * as self from './acctRoute';

...

const acctRmRouter = function(httpReq, httpRes, next) {

    ...

    // call the function using the module
    return self.removeUserEntry(httpReq.body).then((res) => {

    ...

Node.js模块

...

const acctRmRouter = function(httpReq, httpRes, next) {

  ...

  // call the function using module.exports
  return module.exports.removeUserEntry(httpReq.body).then((res) => {

  ...