javascript - 测试子类时如何监视祖父类中的导入函数?

标签 javascript node.js class jasmine sinon

我在监视从 Node 模块导入的函数时遇到问题。我正在测试一个子类,该模块是在祖父类中导入的,我需要查看调用该函数时使用的参数。

代码按预期工作,但我已经用 Jasmine 的内置 spyOnsinon.spy 进行了测试,但两个 spy 都没有被调用。

代码:

// ChildClass.js

const ParentClass = require('./ParentClass');

module.exports = class ChildClass extends ParentClass {

    constructor () {
        super();
        this.foo();
    }

    foo () {
        this.message = this.importGetter('Some input');
    }

};
// ParentClass.js

const GrandparentClass = require('./GrandparentClass');

module.exports = class ParentClass extends GrandparentClass {

    constructor () {
        super();
        this._message = null;
    }

    get message () {
        return this._message;
    }

    set message (value) {
        this._message = value;
    }

};
// GrandparentClass.js

const nodeModuleFunction = require('./nodeModule').nodeModuleFunction;

module.exports = class GrandparentClass {

    get importGetter () {
        return nodeModuleFunction;
    }

};
// nodeModule.js

async function nodeModuleFunction (input) {

    console.error('Do something with input: ', input);

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Returned from node module.');
        }, 300);
    });
}

exports.nodeModuleFunction = nodeModuleFunction;

测试代码:

// test.spec.js

const ChildClass = require('./ChildClass');
const nodeModule = require('./nodeModule');
const sinon = require('sinon');

describe('ChildClass test', () => {

    describe('Jasmine spy', () => {

        it('should call nodeModule.nodeModuleFunction with given value', done => {

            spyOn(nodeModule, 'nodeModuleFunction').and.callThrough();

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalled();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalledWith('Some input');

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });

        });

    });

    describe('Sinon spy', () => {

        it('should call nodeModule.nodeModuleFunction with given value', done => {

            const spy = sinon.spy(nodeModule, 'nodeModuleFunction');

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(spy.called).toBe(true);
            expect(spy.withArgs('Some input').calledOnce).toBe(true);

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });
        });

    });

});

测试结果:

Jasmine started
Do something with input:  Some input

  ChildClass test

    Jasmine spy
      ✗ should call nodeModule.nodeModuleFunction with given value
        - Expected spy nodeModuleFunction to have been called.
        - Expected spy nodeModuleFunction to have been called with [ 'Some input' ] but it was never called.

Do something with input:  Some input
    Sinon spy
      ✗ should call nodeModule.nodeModuleFunction with given value
        - Expected false to be true.
        - Expected false to be true.

根据 Brian 的建议使用解决方案进行编辑:

const nodeModule = require('./nodeModule');

describe('ChildClass test', () => {

    let ChildClass;

    beforeAll(() => {
        spyOn(nodeModule, 'nodeModuleFunction').and.callThrough();  // create the spy...
        ChildClass = require('./ChildClass');  // ...and now require ChildClass
    });

    afterEach(() => {
        nodeModule.nodeModuleFunction.calls.reset();
    });

    describe('Jasmine spy', () => {

        it('should call nodeModule.nodeModuleFunction with given value', done => {

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalled();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalledWith('Some input');

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });

        });

        it('should still call nodeModule.nodeModuleFunction with given value', done => {

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalled();
            expect(nodeModule.nodeModuleFunction).toHaveBeenCalledWith('Some input');

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });

        });

    });

});
const nodeModule = require('./nodeModule');
const sinon = require('sinon');

describe('ChildClass test', () => {

    let spy;
    let ChildClass;

    beforeAll(() => {
        spy = sinon.spy(nodeModule, 'nodeModuleFunction');  // create the spy...
        ChildClass = require('./ChildClass');  // ...and now require ChildClass
    });

    afterEach(() => {
        spy.resetHistory();
    });

    afterAll(() => {
        spy.restore();
    });

    describe('Sinon spy', () => {

        it('should call nodeModule.nodeModuleFunction with given value', done => {

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(spy.called).toBe(true);
            expect(spy.withArgs('Some input').calledOnce).toBe(true);

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });
        });

        it('should still call nodeModule.nodeModuleFunction with given value', done => {

            const object = new ChildClass();

            expect(object.message).not.toBeNull();
            expect(spy.called).toBe(true);
            expect(spy.withArgs('Some input').calledOnce).toBe(true);

            object.message.then(message => {
                expect(message).toBe('Returned from node module.');
                done();
            });
        });

    });

});

最佳答案

GrandparentClass.js 需要 nodeModule.js 并在它运行时获取对 nodeModuleFunction 的引用 。 ..

...所以你只需要确保你的 spy 在它运行之前就位:

const nodeModule = require('./nodeModule');
const sinon = require('sinon');

describe('ChildClass test', () => {

  describe('Sinon spy', () => {

    it('should call nodeModule.nodeModuleFunction with given value', done => {

      const spy = sinon.spy(nodeModule, 'nodeModuleFunction');  // create the spy...
      const ChildClass = require('./ChildClass');  // ...and now require ChildClass

      const object = new ChildClass();

      expect(object.message).not.toBeNull();  // Success!
      expect(spy.called).toBe(true);  // Success!
      expect(spy.withArgs('Some input').calledOnce).toBe(true);  // Success!

      object.message.then(message => {
        expect(message).toBe('Returned from node module.');  // Success!
        done();
      });
    });

  });

});

我更新了 Sinon 测试,但该方法也适用于 Jasmine 测试。

关于javascript - 测试子类时如何监视祖父类中的导入函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55762271/

相关文章:

javascript - 尝试运行应用程序模块 angularjs 时没有错误的空白页面

javascript - 无法解析目录 'bootstrap'

node.js - AWS DynamoDb 查询表

node.js - Google Datastore 部分字符串匹配

ios - 如何确定 iOS (id) 对象的结构?

C# 将类作为参数传递

javascript - 当单击单元格内的链接值发生变化时,如何动态更改表格行背景颜色

javascript - 返回搜索 promise

javascript - 通过 JavaScript 从简单计算中删除 ".00"

c# - 代码找不到类