简体   繁体   English

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

[英]How to test that one function is called before another

I have some tightly coupled legacy code that I want to cover with tests. 我有一些紧密耦合的遗留代码,我想用测试来覆盖。 Sometimes it's important to ensure that one mocked out method is called before another. 有时确保在另一个方法之前调用一个模拟方法很重要。 A simplified example: 一个简化的例子:

function PageManager(page) {
    this.page = page;
}
PageManager.prototype.openSettings = function(){
    this.page.open();
    this.page.setTitle("Settings");
};

In the test I can check that both open() and setTitle() are called: 在测试中,我可以检查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");
    });
});

But setTitle() will only work after first calling open() . 但是setTitle()只能在第一次调用open()之后才能工作。 I'd like to check that first page.open() is called, followed by setTitle() . 我想检查第一个page.open()是否被调用,然后是setTitle() I'd like to write something like this: 我想写这样的东西:

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

But Jasmine doesn't seem to have such functionality built in. 但Jasmine似乎并没有内置这样的功能。

I can hack up something like this: 我可以破解这样的东西:

beforeEach(function() {
    this.page = jasmine.createSpyObj("MockPage", ["open", "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"]);
});

This works, but I'm wondering whether there is some simpler way to achieve this. 这有效,但我想知道是否有一些更简单的方法来实现这一点。 Or some nice way to generalize this so I wouldn't need to duplicate this code in other tests. 或者一些很好的方法来概括这一点,所以我不需要在其他测试中复制此代码。

PS. PS。 Of course the right way is to refactor the code to eliminate this kind of temporal coupling. 当然,正确的方法是重构代码以消除这种时间耦合。 It might not always be possible though, eg when interfacing with third party libraries. 但是,例如,当与第三方库连接时,可能并不总是可能的。 Anyway... I'd like to first cover the existing code with tests, modifying it as little as possible, before delving into further refactorings. 无论如何......我想首先用测试覆盖现有代码,尽可能少地修改它,然后再深入研究进一步的重构。

Try this: 尝试这个:

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');
}

EDIT: I just realized my original answer is effectively the same as the hack you mentioned in the question but with more overhead in setting up a spy. 编辑:我刚刚意识到我的原始答案实际上与您在问题中提到的黑客相同,但在设置间谍方面有更多的开销。 It's probably simpler doing it with your "hack" way: 使用“黑客”方式进行操作可能更简单:

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');
}

I'd like to write something like this: 我想写这样的东西:

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

But Jasmine doesn't seem to have such functionality built in. 但Jasmine似乎并没有内置这样的功能。

Looks like the Jasmine folks saw this post, because this functionality exists . 看起来像Jasmine人看过这篇文章,因为这个功能存在 I'm not sure how long it's been around -- all of their API docs back to 2.6 mention it, though none of their archived older style docs mention it. 我不知道它已经存在了多长时间 - 他们所有的API文档都回到了2.6提到它,尽管他们的档案旧版文档都没有提到它。

toHaveBeenCalledBefore( expected ) toHaveBeenCalledBefore( expected
expect the actual value (a Spy ) to have been called before another Spy . 期望在另一个间谍之前调用实际值( 间谍 )。

Parameters: 参数:

 Name Type Description expected Spy Spy that should have been called after the actual Spy. 

A failure for your example looks like Expected spy open to have been called before spy setTitle . 您的示例的失败看起来像是Expected spy open to have been called before spy setTitle

Create a fake function for the second call that expects the first call to have been made 为第二次调用创建一个假函数,该函数需要进行第一次调用

it("opens page before setting title", function() {

    // When page.setTitle is called, ensure that page.open has already been called
    this.page.setTitle.and.callFake(function() {
        expect(this.page.open).toHaveBeenCalled();
    })

    this.manager.openSettings();
});

使用间谍上的.calls.first().calls.mostRecent()方法检查特定的调用。

Basically did the same thing. 基本上做了同样的事情。 I felt confident doing this because I mocked out the function behaviors with fully synchronous implementations. 我有信心这样做,因为我用完全同步的实现模拟了函数行为。

it 'should invoke an options pre-mixing hook before a mixin pre-mixing hook', ->
    call_sequence = []

    mix_opts = {premixing_hook: -> call_sequence.push 1}
    @mixin.premixing_hook = -> call_sequence.push 2

    spyOn(mix_opts, 'premixing_hook').and.callThrough()
    spyOn(@mixin, 'premixing_hook').and.callThrough()

    class Example
    Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']

    expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
    expect(@mixin.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
    expect(call_sequence).toEqual [1, 2]

Lately I've developed a replacement for Jasmine spies, called strict-spies , which solves this problem among many others: 最近我开发了一个名为strict-spies的 Jasmine间谍的替代品,它解决了许多其他问题:

describe("PageManager.openSettings()", function() {
    beforeEach(function() {
        this.spies = new StrictSpies();
        this.page = this.spies.createObj("MockPage", ["open", "setTitle"]);

        this.manager = new PageManager(this.page);
        this.manager.openSettings();
    });

    it("opens page and sets title to 'Settings'", function() {
        expect(this.spies).toHaveCalls([
            ["open"],
            ["setTitle", "Settings"],
        ]);
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在调用另一个方法之前检查方法是否已调用 - How to check if a method is called before another one got called 在执行另一个功能之前如何检查功能 - How to check a function before execute another one 在另一个函数内部已调用了如何测试一个函数 - How to test a function has been called inside another function Jestjs如何测试在另一个函数中被调用的函数 - Jestjs how to test function being called inside another function 检查在Jasmine测试中是否有一个名为另一个函数的函数 - Check if a function called another function in Jasmine test 如何等待 function 完成后再运行另一个或 rest 的代码? - How to wait for function to finish before running another one or rest of the code? 如何测试是否在异步调用的测试函数B中调用了函数A - How to test if function A was called inside the test function B if it was called asynchronously 每次执行另一个功能之前如何检查一个功能是否已被调用 - How to check if a function has been called before executing another function everytime 一个函数在另一个函数完成之前加载 - One function loads before another one is finished 如何对另一个函数中的一个函数调用进行单元测试? - How I can I unit test that a function inside another function was called?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM