javascript – 如何测试一个函数在另一个函数之前被调用

我有一些紧密耦合的遗留代码,我想用测试来覆盖.有时确保在另一个方法之前调用一个模拟方法很重要.一个简化的例子:
function PageManager(page) {
    this.page = page;
}
PageManager.prototype.openSettings = function(){
    this.page.open();
    this.page.setTitle("Settings");
};

在测试中,我可以检查open()和setTitle()是否都被调用

describe("PageManager.openSettings()",function() {
    beforeEach(function() {
        this.page = jasmine.createSpyObj("MockPage",["open","setTitle"]);
        this.manager = new PageManager(this.page);
        this.manager.openSettings();
    });

    it("opens page",function() {
        expect(this.page.open).toHaveBeenCalledWith();
    });

    it("sets page title to 'Settings'",function() {
        expect(this.page.setTitle).toHaveBeenCalledWith("Settings");
    });
});

但是setTitle()只能在第一次调用open()之后才能工作.我想检查第一个page.open()是否被调用,然后是setTitle().我想写这样的东西:

it("opens page before setting title",function() {
    expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle);
});

但Jasmine似乎并没有内置这样的功能.

我可以破解这样的东西:

beforeEach(function() {
    this.page = jasmine.createSpyObj("MockPage","setTitle"]);
    this.manager = new PageManager(this.page);

    // track the order of methods called
    this.calls = [];
    this.page.open.and.callFake(function() {
        this.calls.push("open");
    }.bind(this));
    this.page.setTitle.and.callFake(function() {
        this.calls.push("setTitle");
    }.bind(this));

    this.manager.openSettings();
});

it("opens page before setting title",function() {
    expect(this.calls).toEqual(["open","setTitle"]);
});

这有效,但我想知道是否有一些更简单的方法来实现这一点.或者一些很好的方法来概括这一点,所以我不需要在其他测试中复制此代码.

PS.当然,正确的方法是重构代码以消除这种时间耦合.但是,它可能并不总是可能的,例如,与第三方库连接时.无论如何…我想首先用测试覆盖现有代码,尽可能少地修改它,然后再深入研究进一步的重构.

解决方法

试试这个:
it("setTitle is invoked after open",function() {
    var ordercop = jasmine.createSpy('ordercop');
    this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
        ordercop('fisrtInvoke');
    });

    this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
        ordercop('secondInvoke');
    });

    this.manager.openSettings();

    expect(ordercop.calls.count()).toBe(2);
    expect(ordercop.calls.first().args[0]).toBe('firstInvoke');
    expect(ordercop.calls.mostRecent().args[0]).toBe('secondInvoke');
}

编辑:我刚刚意识到我的原始答案实际上与你在问题中提到的黑客一样,但是在设置间谍方面有更多的开销.使用“黑客”方式进行操作可能更简单:

it("setTitle is invoked after open",function() {
    var ordercop = []
    this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
        ordercop.push('fisrtInvoke');
    });

    this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
        ordercop.push('secondInvoke');
    });

    this.manager.openSettings();

    expect(ordercop.length).toBe(2);
    expect(ordercop[0]).toBe('firstInvoke');
    expect(ordercop[1]).toBe('secondInvoke');
}

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...