2009-09-13 11 views
15

To wydaje się być czymś prostym, ale nie mogę sprawić, żeby zadziałało.Jak sprawdzić, czy inna metoda w klasie została wywołana za pomocą Moq

Mam klasę z metodą Save, która po prostu wywołuje inną metodę ShouldBeCalled(). Chcę sprawdzić, czy jeśli wywołasz Save(), to druga metoda ShouldBeCalled() jest wykonywana co najmniej raz. Myślałem, że mogę zrobić co następuje.

public class ClassA 
{ 
    public virtual void Save() 
    { 
     ShouldBeCalled(); 
    } 

    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.Object.Save(); 

     mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
    } 
} 

Ale pojawia się wyjątek „Oczekiwany wezwanie na mock co najmniej raz, ale nigdy nie została wykonana: x => x.ShouldBeCalled()”

To tylko przypuszczenie, ale jest Moq nadrzędnymi Metoda Save() z własną wersją, która ignoruje wszystko, co mam w metodzie Save object.

Odpowiedz

28

Masz ten problem, ponieważ kpisz z testowanego produktu. To nie ma sensu.

Masz rację, że Moq zastąpi implementację twojej metody własną. Powodem jest to, że używasz Moq do kpienia z klasy , którą testujesz, dzwoniąc pod numer, a nie od klasy, którą sam testujesz.

Badanie to byłoby właściwe, jeśli kod zostały zaprojektowane wygląda następująco:

public class ClassA 
{ 
    BusinessLogicClass bl; 
    public ClassA(BusinessLogicClass bl) 
    { 
     this.bl = bl; 
    } 

    public void Save() 
    { 
     bl.ShouldBeCalled(); 
    } 
} 

public class BusinessLogicClass 
{ 
    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

I tu jest poprawny test tej metody teraz:

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_ShouldCallShouldBeCalled() 
    { 
     //Arrange 
     var mockBLClass = new Mock<BusinessLogicClass>(); 
     mockBLClass.Setup(x => x.ShouldBeCalled()).Verifyable(); 

     //Act  
     ClassA classA = new ClassA(mockBLClass.Object); 
     classA.Save(); 

     //Assert 
     mockBLClass.VerifyAll(); 
    } 
} 

Kluczem lekcja jest to, że mock/Zauważ, co twój test musi uruchomić, a nie to, co sam testujesz.

Nadzieja to pomaga, Anderson

+0

+1: Świetna odpowiedź, wraz z wyłudzaniem przykładów zależności i kodu! Zauważ, że alternatywą dla metody wirtualnej byłoby dodanie interfejsu IBusinessLogic i przekazanie tego. – TrueWill

+0

Interfejs IBusinessLogic jest zdecydowanie najlepszym sposobem, aby przejść tutaj, ale nie chciałem zbytnio w to zagłębiać. –

+1

Dzięki za wspaniałą odpowiedź. Miałem przeczucie, że próbuję zrobić coś złego z moim podejściem i teraz wiem, że byłem :) – Adam

2

Tak, można to zrobić. Jednak musisz dodać linię kodu, aby śledzić Moq niezależnie od tego, czy rzeczywiście wywołano metodę ShouldBeCalled.

Coś jak poniżej będzie działać:

var mockClassA = new Mock<ClassA>(); 
mockClassA.Setup(x => x.ShouldBeCalled()).Verifiable();  
mockClassA.Object.Save();  
mockClassA.Verify(x => s.ShouldBeCalled(), Times.AtLeastOnce()); 

Sposób konfiguracji ustawia oczekiwania. Kiedy dzwonisz do Verify, pytasz Moq, aby zweryfikować te oczekiwania. Jeśli nie wykonasz wywołania Setup w celu stworzenia oczekiwań dla metody ShouldBeCalled, wówczas Moq nie uważa, że ​​jest on możliwy do śledzenia, a zatem nie powiedzie się, gdy spróbujesz go zweryfikować.

+0

Próbowałem powyższego, ale nadal widzę ten sam błąd. To, co mówisz, ma sens, dlatego nie mogę zrozumieć, dlaczego to nie działa. – Adam

+0

Czy możesz zmienić deklarację konfiguracji, aby sfałszować zwrot? Na przykład mockClassA.Setup (x => x.ShouldBeCalled()). Zwraca (...). –

+0

Konfiguracja jest tu nadmiarowa, ponieważ nie używasz Strict. Ale +1 w ogóle –

4

Spróbuj użyć CallBase = true, a następnie fałszywe. Uruchomiłem twój kod i działa.

var mockClassA = new Mock<ClassA>(); 
mockClassA.CallBase = true; 
mockClassA.Object.Save(); 
mockClassA.CallBase = false; 
mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
+0

To działa, wow! Jestem nowy w Moq, więc nie mam pojęcia, czy to dobra praktyka czy nie, ale działa. Zauważ, że działa to tylko wtedy, gdy kpisz z konkretnej klasy za pomocą metod wirtualnych, tak jak w przypadku ClassA. Jeśli klasa implementuje interfejs i tworzysz próbę z interfejsu, to nie będzie działać. np. Jeśli ClassA zaimplementował IClassA, wówczas pierwsza linia odpowiedzi Erica zmieni się na "var mockClassA = new Mock ();" Prześmiewanie interfejsu w ten sposób spowoduje niepowodzenie testu, ponieważ funkcja ShouldBeCalled nigdy nie jest wywoływana. Bardzo podobny problem z oryginalnym kodem w pytaniu Adama. –

1

Możesz stosować metody pośrednie w testowanym systemie, używając CallBase.

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     // Arrange 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.CallBase = true; // this will call real methods unless the method is mocked/stubbed. 
     mockClassA.Setup(a => a.ShouldBeCalled()); 

     // Act 
     mockClassA.Save(); 

     // Assert 
     mockClassA.Verify(a => a.ShouldBeCalled(), Times.Once()); 
    } 
} 
+0

hiperłącze w tej odpowiedzi jest nieaktualne. – buffjape

Powiązane problemy