2009-02-25 12 views
12

Jestem naprawdę nowy w szyderstwie i próbuję zastąpić prywatne pole próbnym obiektem. Obecnie instancja pola prywatnego jest tworzona w konstruktorze. Mój kod wygląda ...Jak wyśmiać prywatne pole?

public class Cache { 
    private ISnapshot _lastest_snapshot; 

    public ISnapshot LatestSnapshot { 
     get { return this._lastest_snapshot; } 
     private set { this._latest_snapshot = value; } 
    } 

    public Cache() { 
     this.LatestSnapshot = new Snapshot(); 
    } 

    public void Freeze(IUpdates Updates) { 
     ISnapshot _next = this.LastestSnapshot.CreateNext(); 
     _next.FreezeFrom(Updates); 
     this.LastestSnapshot = _next; 
    } 

} 

Co staram się zrobić, to stworzyć test jednostki, która twierdzi ISnapshot.FreezeFrom(IUpdates) nazywany jest od wewnątrz Cache.Freeze(IUpdates). Zgaduję, że powinienem zastąpić prywatne pole _latest_snapshot fałszywym obiektem (może błędnym założeniem?). Jak mam to zrobić, zachowując konstruktor bez parametrów i nie stosując zestawu LatestSnapshot?

Jeśli mam zamiar napisać test w niewłaściwy sposób, proszę również zwrócić uwagę.

Sam fakt wykonania ISnapshot.FreezeFrom sam nazywa się dziedziczeniem innych metod z głębokim wykresem obiektów, więc nie jestem zbyt zainteresowany przedstawieniem wykresu obiektu.

Z góry dziękuję.

Odpowiedz

16

jestem prawie powołując technik z "Working Effectively with Legacy Code":

  1. Podklasa swoją klasę w badanej jednostki i zastąpić zmienną prywatną z mock obiektu w nim (dodając publiczny setter lub konstruktora) . Prawdopodobnie musisz ustawić zmienną chronioną.
  2. Utwórz chroniony program pobierający dla tej zmiennej prywatnej i zastąp ją w podklasie testowej, aby zwrócić obiekt próbny zamiast rzeczywistej zmiennej prywatnej.
  3. Utwórz metodę chronionej fabryki do tworzenia obiektu ISnapshot i nadpisaj ją w podklasie testowej, aby zwrócić instancję symulowanego obiektu zamiast prawdziwego. W ten sposób konstruktor uzyska właściwą wartość od początku.
  4. Wykonaj konstruktor Parametri, aby pobrać instancję ISnapshot.
4

Nie sądzę, że trzeba sfałszować prywatne zmienne składowe. Czy cały pomysł kpienia z tego, że publiczny interfejs dla obiektu działa zgodnie z oczekiwaniami? Zmienne prywatne są szczegółami implementacji, których nie dotyczą makiety.

+4

Co jeśli testowana klasa używa prywatnego pola reprezentującego bazę danych. Jak wyśmiać to pole bazy danych? – Vanuan

+0

chcesz poznać schemat repozytorium, który zasadniczo ukrywa magazyn danych za zestawem interfejsów, dzięki czemu można je łatwo wyśmiewać i przetestować. – Jason

+1

Nadal jednak, jak konstruujesz to wyśmiewane repozytorium i przekazujesz je konsumentowi? Czy potrzebujesz settera lub parametru konstruktora? – Vanuan

3

Nie jestem pewien, czy możesz to zrobić. Jeśli chcesz przetestować _next, prawdopodobnie będziesz musiał przekazać go jako parametr, a następnie w jednostce testowej przejść w obiekcie Mock, który możesz następnie przetestować za pomocą oczekiwań. To właśnie bym robił, gdybym próbował to zrobić w Moq.

Jako przykład tego, co mógłbym spróbować użyć ramy MOQ:

Mock<ISnapshot> snapshotMock = new Mock<ISnapshot>(); 
snapshotMock.Expect(p => p.FreezeFrom(expectedUpdate)).AtMostOnce(); 
Cache c = new Cache(snapshotMock.Object); 
c.Freeze(expectedUpdate); 

Uwaga: Nie próbowałem skompilować powyższy kod. Jest tam po to, aby dać przykład tego, jak podchodzę do rozwiązania tego problemu.

1

Ta odpowiedź może być prosta, ale patrząc na kod, jest jakiś sposób, w jaki ISnapshot.FreezeFrom(IUpdates) nie zostanie wywołany? Wygląda na to, że chcesz potwierdzić coś, co zawsze będzie prawdą.

Jak mówi Jason, szyderczy jest przeznaczona dla sytuacji, w których klasa zależy SomeInterface zrobić to praca, a chcesz przetestować YourClass w oderwaniu od którykolwiek realizacja SomeInterface faktycznie korzysta przy starcie.

+0

jeśli ci się podoba, to w górę :) – Jason

1

Pytanie, które należy zadać, brzmi: jakie są zewnętrzne efekty, jeśli to zadziałało?

Co stanie się z tymi migawkami? Jedna opcja może zainicjować pamięć podręczną z jej pierwszą migawką z zewnątrz, powiedzmy w konstruktorze. Innym może być naśmiewanie się z tego, co wywołuje Snapshot, poza kręgiem podręcznym. To zależy od tego, co cię to obchodzi.

1

Być może jest za późno na odpowiedź. W każdym razie. Miałem również podobny problem.

public class Model 
{ 
    public ISomeClass XYZ{ 
     get; 
     private set; 
     } 
} 

Potrzebowałem ustawić wartość XYZ w moim przypadku testowym. Rozwiązałem problem za pomocą tego synteksu.

Expect.Call(_model.XYZ).Return(new SomeClass()); 
_repository.ReplayAll(); 

W przypadku powyżej, możemy to zrobić jak ten

Expect.Call(_cache.LatestSnapshot).Return(new Snapshot()); 
_repository.ReplayAll(); 
-2

Włącz Cache do szablonu, jak pokazano poniżej.

template <typename T=ISnapshot> 
public class Cache { 
    private T _lastest_snapshot; 

    public T LatestSnapshot { 
     get { return this._lastest_snapshot; } 
     private set { this._latest_snapshot = value; } 
    } 

    public Cache() { 
     this.LatestSnapshot = new Snapshot(); 
    } 

    public void Freeze(IUpdates Updates) { 
     T _next = this.LastestSnapshot.CreateNext(); 
     _next.FreezeFrom(Updates); 
     this.LastestSnapshot = _next; 
    } 

} 

W kodzie produkcyjnym zrobić:

Cache<> foo;//OR 
Cache<ISnapshot> bar; 

W kodzie testowym zrobić:

Cache<MockSnapshot> mockFoo; 
+0

zły język: C#, nie C++ – skolima

0

Będziesz prawdopodobnie byłaby swoją klasę tak, aby mogła ona być wstrzyknięto inna zależność dla ISnapshot. Twoja klasa pozostanie działać tak samo.

public class Cache { 
private ISnapshot _lastest_snapshot; 

public ISnapshot LatestSnapshot { 
    get { return this._lastest_snapshot; } 
    private set { this._latest_snapshot = value; } 
} 

public Cache() : this (new Snapshot()) { 
} 

public Cache(ISnapshot latestSnapshot) { 
    this.LatestSnapshot = latestSnapshot; 
} 

public void Freeze(IUpdates Updates) { 
    ISnapshot _next = this.LastestSnapshot.CreateNext(); 
    _next.FreezeFrom(Updates); 
    this.LastestSnapshot = _next; 
} 

} 
0

Możesz po prostu dodać metodę "setSnapshot (ISnapshot)" do pamięci podręcznej za pomocą wyśmiewanej instancji klasy.

Można również dodać konstruktora, który wykonuje ISnapshot.