2009-11-25 7 views
11

Mam klasę TimeMachine, która podaje mi bieżące wartości daty/czasu. Klasa wygląda następująco:Stub jedna metoda klasy i niech inne rzeczywiste metody używają tego zglikowanego

public class TimeMachine 
{ 
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; }; 
    public virtual DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; }; 
    public virtual TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; }; 
} 

Chciałbym użyć TimeMachine niedopałek w moich testach w taki sposób, że ja po prostu odgałęzienie metodę GetCurrentDateTime i niech inni 2 metody użyć zgaszone GetCurrentDateTime metody tak jak ja nie trzeba odcinać wszystkich trzech metod. Próbowałem napisać test tak:

var time = MockRepository.GenerateStub<TimeMachine>(); 
time.Stub(x => x.GetCurrentDateTime()) 
    .Return(new DateTime(2009, 11, 25, 12, 0, 0)); 
Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate()); 

Ale test kończy się niepowodzeniem. GetCurrentDate zwraca default(DateTime) zamiast wewnętrznego połączenia z kodem GetCurrentDateTime.

Czy istnieje jakieś podejście, które mógłbym zastosować, aby osiągnąć takie zachowanie, czy jest to tylko kilka podstawowych cech koncepcyjnych RhinoMocks, których obecnie nie łapię? Wiem, że mogłem po prostu pozbyć się tych dwóch metod i wstawić użycie, ale chciałbym się dowiedzieć, czy to w ogóle możliwe.

Odpowiedz

4

Właśnie się dowiedziałem, że można to osiągnąć, nie używając wirtualnego na tych dwóch metodach - chroni to metody przed przesłonięciem podczas generowania kodu pośredniczącego.

public class TimeMachine 
{ 
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; }; 
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; }; 
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; }; 
} 

Test przechodzi teraz.

+3

Może to sprawić, że test przejdzie pomyślnie, ale metody powinny być wirtualne tylko wtedy, gdy klasa ma działać jako klasa podstawowa, a metoda jest specjalnie zaprojektowana, aby była nadpisana. Nie powinno to być wirtualne, tylko ze względu na testy jednostkowe. –

+0

Dlaczego nie? Dlaczego nie uczynić wszystkich metod wirtualnymi ze względu na rozszerzalność? :) I chociaż akceptuję twoje argumenty, GateCurrentDateTime jest imo całkowicie OK, aby zostać zastąpione. – Buthrakaur

+0

Zgadzam się. Dlaczego nie? Cóż prawie się zgadzam. W Javie każda metoda jest wirtualna. Ale wtedy nie dano ci czasu na kompilację, że nadpisałeś właściwą metodę. To znaczy, dopóki adnotacje nie zapiszą dnia. Ale .Net nie potrzebuje adnotacji, ponieważ zmusza do zadeklarowania metody jako wirtualnej i metody nadpisującej jako nadpisania. Ale byłoby miło, gdyby było lepsze rozwiązanie tego problemu. – uriDium

1

Kik podpowiedzi dostarcza odpowiedzi na metodę i wywołania właściwości, nie wie nic o rzeczywistej implementacji TimeMachine. Obawiam się, że będziesz musiał ustawić wyniki dla każdej z 3 metod (lub dla konkretnej metody, którą chciałbyś przetestować).

+1

Nie sądzę, że masz rację w przypadku krępowania prawdziwych klas (nie w przypadku interfejsów). Karczek właśnie wywodzi się z klasy i unieważnia jego metody. Jeśli metoda nie jest wirtualna, nie można jej przesłonić i pozostaje ona funkcjonalna, tak jak została zaimplementowana przez klasę rzeczywistą. – Buthrakaur

+0

Jesteś tego pewien? –

9

zmienić TimeMachine do klasy abstrakcyjnej:

public abstract class TimeMachine 
{ 
    public abstract DateTime GetCurrentDateTime(); 
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; }; 
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; }; 
} 

do celów produkcyjnych, można stworzyć konkretną realizację TimeMachine takiego:

public class SystemTimeMachine : TimeMachine 
{ 
    public override DateTime GetCurrentDateTime() 
    { 
     return DateTime.Now; 
    } 
} 

Wszystkie klasy zużywające TimeMachine można teraz wstrzyknięto abstrakcja, ale w produkcji możesz połączyć swój wykres obiektów z SystemTimeMachine.

1

Nie jestem pewien, której wersji Rhino.Mocks używasz, ale chciałbym tylko uczynić metodę GetCurrentDateTime() wirtualną (jak sugerował wcześniejszy plakat), a następnie utworzyć próbny obiekt przy użyciu PartialMock (). Istnieje wiele sposobów, aby ustawić rzeczy w górę, ale dodaje powinno działać:

var mocks = new MockRepository(); 
var time = mocks.PartialMock<TimeMachine>(); 
using (mocks.Record()) 
{ 
    Expect.Call(time.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0)); 
} 
using (mocks.Playback()) 
{ 
    Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate()); 
} 
+0

Częściowe Moki i to użycie rekordu/odtwarzania jest imo przestarzałe w RhinoMocks. Ale to rozwiązanie jest prawdopodobnie równoznaczne z moją poprzednią odpowiedzią. – Buthrakaur

8

Jeśli metoda jest oznaczona jako virtual, zalążek nie wywoła oryginalną metodę, nawet jeśli nie skrótową metody. Można wymusić RhinoMocks nazwać oryginalną metodę wykonując:

var time = MockRepository.GenerateStub<TimeMachine>(); 
time.Stub(x => x.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0)); 

time.Stub(x => x.GetCurrentDate()).CallOriginalMethod(OriginalCallOptions.NoExpectation); 

Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate()); 

To trzecia (rozdzielone) linia sprawia, że ​​RhinoMocks wywołać bazowych, oryginalną metodę.

+0

+1 za jedyną realną odpowiedź na to pytanie (choć od 4 lat) – Heliac