2011-06-24 8 views
20

Miałem kilka interesujących udręk, próbując sprawdzić, czy poglądy zostały poprawnie powiązane z wydarzeniami. W szkielecie zazwyczaj wiążemy zdarzenia w metodzie initialize, używając czegoś podobnego do: something.bind("change", this.render);. W moim teście chcę się upewnić, że to powiązanie jest skonfigurowane, więc wykonałem następujące czynności:Testowanie aplikacji backbone.js z jaśminem - jak przetestować wiązania modelu w widoku?

this.myView = new MyView(); 
spyOn(this.myView, "render");; 
this.legendView.groupData.trigger("change"); 
expect(this.legendView.render).toHaveBeenCalled(); 

Ale to nie zadziała. Ponieważ powiązanie występuje w funkcji inicjowania MyView, zdarzenie get jest powiązane z funkcją renderowania myView W TYM CZASIE. Kiedy dodajesz swojego szpiega, zawija on funkcję renderowania i ustawia ją z powrotem na miejscu w myView.render. Ale zamknięcie stworzone przez pierwsze wiązanie nadal istnieje i jesteśmy całkowicie spoczęci. Więc co możemy z tym zrobić? To, co zrobiłem, jest przenieść mój wiążą wezwanie do funkcji indywidualne, coś jak:

myView = Backbone.View.extend({ 
initialize: function(){ 
    _.bindAll(this, "render"); 
    this.initialize_model_bindings(); 
}, 
initialize_model_bindings: function(){ 
    something.bind("change", this.render); 
}, 
render: function(){ //... } 
}); 

i moja próba wówczas wygląda następująco:

this.myView = new MyView(); 
spyOn(this.myView, "render"); 
this.myView.initialize_model_bindings(); 
this.legendView.groupData.trigger("change"); 
expect(this.legendView.render).toHaveBeenCalled(); 

To działa, ale szukam lepszego rozwiązania . Dzięki

+0

Czy możesz wyjaśnić tę kwestię: "Ale zamknięcie stworzone przez pierwsze wiązanie nadal istnieje, a my jesteśmy całkowicie sprowadzeni" ponownie? Mam ten sam problem i próbuję zrozumieć, co się dzieje. Dzięki. –

+1

Po wywołaniu 'new MyView()' wywoływana jest metoda initialize, a funkcja renderowania istniejąca w obiekcie w tym czasie jest powiązana ze zdarzeniem change. Od tego momentu nie można uzyskać dostępu do funkcji renderowania związanej ze zdarzeniem zmiany - jest ona zamknięta w zamknięciu. – idbentley

+0

Dzięki za wyjaśnienia. –

Odpowiedz

5

Zamiast szpiegować wywołania zwrotnego, możesz spróbować szpiegować coś. Następnie przetestuj, że bind został wywołany w/odpowiednie argumenty. To działa dla mnie jak dotąd. Używam sinon.js zamiast wbudowanych szpiegów jaśminu. sinon.js sprawia, że ​​testowanie dla argumentów przekazywanych do wywołania metody w stosie tych samych wywołań metod jest nieco łatwiejsze (np. zestaw wywołań do wiązania w init widoku). Więc nie testowałem tego pomysłu z jaśminem, ale uważam, że powinno to być możliwe.

spyOn(this.legendView.groupData, 'bind'); 
this.myView = new MyView(); 
expect(this.legendView.groupData.mostRecentCall.args).toEqual('change', this.myView.render); // example!! only works if testing a single call to bind or the last call in a series (ie mostRecentCall) 

i W/sinon.js

sinon.spy(this.legendView.groupData, 'bind'); 
this.myView = new MyView(); 
expect(this.legendView.groupData.bind.calledWith('change', this.myView.render); // works w/ any number of calls to bind 
3

I rozwiązać ten problem poprzez szpiegostwo na funkcję wzywano mojego funkcji renderowania. Więc w przykładzie:

myView = Backbone.View.extend({ 
    initialize: function(){ 
     _.bindAll(this, "render"); 
     something.bind("change", this.render); 
    }, 
    someOtherFunction: function(){}, //this function only called from render 
    render: function(){ this.someOtherFunction(); /* rest of render function */ } 
}); 

Test wygląda następująco:

this.myView = new MyView(); 
spyOn(this.myView, "someOtherFunction"); 
this.myView.something.trigger("change"); 
expect(this.myView.someOtherFunction).toHaveBeenCalled(); 

wtedy napisałem oddzielny test na cokolwiek someOtherFunction robi.

+0

To nie wydaje się straszne rozwiązanie, ale moje funkcje renderowania zmieniają się dość często. Czuję, że bardzo często aktualizowałbym swoje specyfikacje, gdybym to zrobił. – idbentley

2

Powinieneś rozważyć spojrzenie na Sinon.js. Możesz wywołać funkcję makra lub makiety wywołania metody render(), a nawet nie musisz się martwić o "someOtherFunction()".

1

ten jest prawdopodobnie zbyt ściśle sprzężony z wewnętrznymi kręgosłup, ale można ręcznie sprawdzić łańcuch oddzwonienia:

expect(this.legendView.groupData._callbacks['change']).toContain(this.myView.render) 
12

udało mi się osiągnąć to za pomocą prototypowego łatanie. Zanim utworzysz instancję widoku, prototyp konstruktora: spyOn.

spyOn(MyView.prototype, 'changeSelected'); 
var view = new MyView(); 
view.selectSomething(); 
expect(view.changeSelected).toHaveBeenCalled(); 
+0

z jakiegoś powodu działa to dla mnie tylko przy użyciu 'sinon.spy (...)'. dzięki za tę wskazówkę! – fifigyuri

+0

Nie zdziwiłbym się, gdyby w zeszłym roku zmieniono api sinona ... – mjtamlyn

0

wpadłem na ten sam problem i zmieniłem kod Widoki z:

this.model.on('change', this.render, this); 

do:

this.model.on('change', function() { 
    this.render(); 
}, this); 

i moich testów jaśminu działa zgodnie z oczekiwaniami.

Powiązane problemy