2012-01-04 13 views
57

Chcę przetestować, czy wywoływana jest następująca metoda w moim konstruktorze obiektów JavaScript. Z tego, co widziałem w dokumentacji Jasmine, mogę śledzić metodę konstruktora i mogę śledzić metody po utworzeniu obiektu, ale nie mogę wydawać się być w stanie szpiegować metody zanim obiekt zostanie skonstruowany.Jaśmin - śledzenie wywołania metody w konstruktorze

Obiekt:

Klass = function() { 
    this.called_method(); 
}; 

Klass.prototype.called_method = function() { 
    //method to be called in the constructor. 
} 

chcę zrobić coś takiego w specyfikacji:

it('should spy on a method call within the constructor', function() { 
    spyOn(window, 'Klass'); 
    var obj = new Klass(); 
    expect(window.Klass.called_method).toHaveBeenCalled(); 
}); 

Odpowiedz

98

Spy bezpośrednio na metodzie prototype:

describe("The Klass constructor", function() { 
    it("should call its prototype's called_method", function() { 
     spyOn(Klass.prototype, 'called_method'); //.andCallThrough(); 
     var k = new Klass(); 
     expect(Klass.prototype.called_method).toHaveBeenCalled(); 
    }); 
}); 
+3

dziękuję za to. najlepszy opis jaki widziałem przez cały dzień na ten temat. – Subimage

+0

Miałem przeczucie, że istnieje uzasadnione rozwiązanie tego problemu. Dziękuję Ci. – BradGreens

+0

Istnieją dwa problemy z tym podejściem. Po pierwsze, powoduje to przeciek pamięci - metoda named_method będzie śledzić wszystkie przyszłe instancje klasy Klass, a ponieważ będzie wykonywanych więcej połączeń, wzrośnie zużycie pamięci przez szpiega. Po drugie, a co ważniejsze, potencjalnie powoduje to wiele testów, które przesłuchują Klassa, aby wchodzić w interakcje ze sobą, ponieważ szpieg został już wezwany. Powinieneś upewnić się, że szpieg został usunięty lub Klass.prototype.called_method zresetowany do oryginalnej metody na końcu sprawy testowej. – alecmce

11

Ogólnie zgadzam z odpowiedzią Dave'a Newtona powyżej. Jednak istnieją pewne skrajne przypadki tego podejścia, które należy wziąć pod uwagę.

Take odmianę do rozwiązania Dave'a, z innym przypadku testowego:

// production code 
var Klass = function() { 
    this.call_count = 0; 
    this.called_method(); 
}; 
Klass.prototype.called_method = function() { 
    ++this.call_count; 
}; 

// test code 
describe("The Klass constructor", function() { 
    it("should call its prototype's called_method", function() { 
    spyOn(Klass.prototype, 'called_method'); 
    var k = new Klass(); 
    expect(k.called_method).toHaveBeenCalled(); 
    }); 
    it('some other test', function() { 
    var k = new Klass(); 
    expect(k.call_count).toEqual(1); 
    }); 
}); 

Drugi test zakończy się niepowodzeniem, ponieważ konfiguracja szpieg w pierwszej próbie utrzymuje poza granice testowych do drugiej metody; Metoda named_method nie zwiększa wartości call_count, więc this.call_count nie ma sobie równych 1. Można również wymyślić scenariusze z fałszywymi pozytywami - testy, które kończą, które nie powinny. Poza tym, ponieważ szpieg pozostaje, im więcej utworzonych instancji Klass, tym większa ilość pamięci, którą szpieg zużyje, ponieważ szpieg zapisuje każde wywołanie metody called_method. To prawdopodobnie nie stanowi problemu w większości przypadków, ale powinieneś być tego świadomy, na wszelki wypadek.

Prostym rozwiązaniem tego problemu byłoby upewnienie się, że szpieg został usunięty po jego użyciu. To może wyglądać nieco brzydki, ale coś jak to działa:

// test code 
describe("The Klass constructor", function() { 
    it("should call its prototype's called_method", function() { 
    var spy = jasmine.createSpy('called_method'); 
    var method = Klass.prototype.called_method; 
    Klass.prototype.called_method = spy; 
    var k = new Klass(); 
    expect(spy).toHaveBeenCalled(); 
    Klass.prototype.called_method = method; 
    }); 

[UWAGA - trochę opinia skończyć] Lepszym rozwiązaniem byłoby zmienić sposób pisać kod produkcyjny, aby kod łatwiejsze do testowania. Co do zasady szpiegowanie prototypów jest prawdopodobnie zapachem kodu, którego należy unikać. Zamiast tworzyć instancję zależności w konstruktorze, należy je wstrzyknąć. Zamiast wykonywać inicjalizację w konstruktorze, należy odłożyć do odpowiedniej metody init.

+1

+1, dzięki za dodatkowe informacje. Myślę, że w dolnej sekcji "WSKAZÓWKA" podsumowuje się wszystko; jest prawie pewne, że powinno to zostać naprawione gdzie indziej niż w teście. –

+0

Działa z najnowszymi wersjami jaśminu. –

+0

Tak, myślę, że zamknęli to jeszcze jakiś czas temu. Nadal warto unikać szpiegowania prototypów z zasady. – alecmce

Powiązane problemy