2014-10-30 4 views
5

Ich habe einige eng gekoppelten Legacy-Code, den ich mit Tests abdecken möchte. Manchmal ist es wichtig sicherzustellen, dass eine verspottete Methode vor einer anderen aufgerufen wird. Ein vereinfachtes Beispiel:Wie zu testen, dass eine Funktion vor einem anderen aufgerufen wird

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

Im Test kann ich, dass beide überprüfen open() und setTitle() genannt werden:

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

Aber setTitle() funktioniert nur nach dem ersten open() aufrufen. Ich möchte überprüfen, dass zuerst page.open() aufgerufen wird, gefolgt von setTitle(). Ich möchte so etwas schreiben:

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

Aber Jasmin scheint nicht in eine solche Funktionalität gebaut haben

ich so etwas wie dieses zerhacken kann.

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"]); 
}); 

Diese funktioniert, aber ich frage mich, ob es einen einfacheren Weg gibt, dies zu erreichen. Oder eine nette Möglichkeit, dies zu verallgemeinern, damit ich diesen Code nicht in anderen Tests kopieren muss.

PS. Natürlich ist der richtige Weg, den Code umzuformen, um diese Art der zeitlichen Kopplung zu eliminieren. Es ist jedoch nicht immer möglich, z.B. wenn Schnittstellen mit Bibliotheken von Drittanbietern hergestellt werden. Wie auch immer ... Ich möchte zuerst den vorhandenen Code mit Tests abdecken und ihn so wenig wie möglich modifizieren, bevor ich weitere Refactorings durchführe.

+0

Gibt es etwas, das Sie geltend machen können, außer dass 'open' aufgerufen wurde? Wie einige DOM-Knoten ändern oder andere "globale" Daten? –

+0

Nein. Der springende Punkt ist, dass ich mich darüber lustig mache - ich möchte nicht, dass das eigentliche 'open' aufgerufen wird, besonders weil es einen globalen Zustand beeinflussen könnte. –

+0

Mögliches Duplikat von [Gibt es eine Möglichkeit, die Reihenfolge der Spionagedurchführungen mit Jasmine zu überprüfen?] (Https://stackoverflow.com/questions/20055781/is-there-a-way-to-verify-the-order-of -spy-executions-with-jasmine) – carpeliam

Antwort

3

Versuchen Sie folgendes:

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

Überprüfen Sie die spezifischen Anrufe mit den Methoden .calls.first() und .calls.mostRecent() auf dem Spion.

+0

Das hilft nur beim Vergleich von Aufrufen einer Methode - ob 'foo()' zuerst mit dem Argument '" a "' oder 'b '' aufgerufen wurde - aber es hilft nicht bei der Entscheidung ob die Methode 'foo()' vor der Methode 'bar()' aufgerufen wurde. –

0

Im Grunde genommen das Gleiche. Ich war zuversichtlich, dies zu tun, weil ich das Funktionsverhalten mit vollständig synchronen Implementierungen verspottete.

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] 
1

Ich möchte so etwas schreiben:

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

Aber Jasmin scheint nicht eine solche Funktionalität eingebaut haben

.

Sieht aus wie die Jasmin-Leute sahen diesen Beitrag, weil this functionality exists. Ich bin nicht sicher, wie lange es schon ist - alle ihre API-Dokumente zurück zu 2.6 erwähnen es, obwohl keiner ihrer archivierten älteren Stildokumente es erwähnt.

toHaveBeenCalledBefore (expected)
expect der Ist-Wert (a Spy) wurde vor einem anderen Spy genannt zu haben.

Parameter:

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

Ein Fehler für Ihr Beispiel wie Expected spy open to have been called before spy setTitle aussieht.

0

In letzter Zeit habe ich einen Ersatz für Jasmine Spione entwickelt, genannt strict-spies, die dieses Problem unter vielen anderen löst:

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"], 
     ]); 
    }); 
}); 
1

Erstellen Sie eine gefälschte Funktion für den zweiten Anruf, der erste Anruf erwartet, gemacht wurden

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(); 
}); 
Verwandte Themen