2013-03-10 36 views
6

swoje istnienie kilku miesięcy, odkąd pracuję z kodu java starszych, to tylko niektóre z rzeczy, mam do czynienia z:Jaka jest różnica między szwem a makietą?

  • 0% pokrycia testowego.
  • Ogromne funkcje w niektórych przypadkach widziałem nawet niektóre z ponad 300 linii kodu.
  • Wiele prywatnych metod i przy okazji metod statycznych.
  • Wysoce szczelny połączony kod.

Na początku byłem bardzo zdezorientowany, stwierdziłem, że trudno jest używać TDD w spuściźnie. Po kilku tygodniach kata i ćwiczeniu umiejętności testowania i kpin, mój strach się zmniejszył i czuję się trochę bardziej pewny siebie. Niedawno odkryłem książkę zatytułowaną: working effectivelly with legacy, nie przeczytałem jej, właśnie obejrzałem spis treści i odkryłem coś nowego dla mnie, The Seams. Wydaje się, że jest to bardzo ważne podczas pracy nad dziedzictwem.

Myślę, że to Szwy mogą mi pomóc w zerwaniu zależności i sprawić, że mój kod będzie testowalny, dzięki czemu mogę zwiększyć zasięg kodu i sprawić, że moje urządzenie będzie bardziej precyzyjne.

Ale mam wiele wątpliwości:

  • Czy ktoś może mi wyjaśnić różnicę pomiędzy szwem i makiety?
  • Czy szwy, łamią zasady TDD w odniesieniu do kwestii, które nie dotyczą kodu produkcyjnego, przed testowaniem?
  • Czy możesz pokazać mi prosty przykład porównujący szew i makietę?

Poniżej chciałbym wkleić przykład, który zrobiłem dzisiaj, w którym próbowałem złamać zależność w celu uczynienia kodu testowalnym i wreszcie zwiększenia pokrycia testowego. Byłbym wdzięczny, gdybyś mógł trochę skomentować, jeśli zauważysz jakieś błędy?

ten sposób kod Legacy wyglądało na początku:

public class ABitOfLegacy 
{ 
    private String sampleTitle; 
    String output; 

    public void doSomeProcessing(HttpServletRequest request) { 
    String [] values = request.getParameterValues(sampleTitle); 
     if (values != null && values.length > 0) 
     { 
      output = sampleTitle + new Date().toString() + values[0]; 
     } 

    } 
} 

Gdybym tylko dodać badanej jednostki, która wywołuje, że metody i twierdzi, że zmienna wyjściowa, ma pewną wartość po zakończeniu rozmowy, a potem popełniłbym błąd, ponieważ nie jestem testem jednostkowym, przeprowadzałbym testy integracyjne. Więc to, co muszę zrobić, to pozbyć się zależności, którą mam w parametrze. Aby to zrobić, ja zastąpić parametr z interfejsem:

public class ABitOfLegacy 
{ 
    private String sampleTitle; 
    String output; 

    public void doSomeProcessing(ParameterSource request) { 
    String [] values = request.getParameters(sampleTitle); 
     if (values != null && values.length > 0) 
     { 
      output = sampleTitle + new Date().toString() + values[0]; 
     } 
    } 

} 

ten sposób interfejs wygląda następująco:

public interface ParameterSource { 
    String[] getParameters(String name); 
} 

Następną rzeczą zrobić, to stworzyć własną implementację tego interfejsu, ale to HttpServletRequest jako zmienną globalną i wdrożyć metody interfejsu za pomocą metody/s HttpServletRequest:

public class HttpServletRequestParameterSource implements ParameterSource { 

    private HttpServletRequest request; 

    public HttpServletRequestParameterSource(HttpServletRequest request) { 
     this.request = request; 
    } 

    public String[] getParameters(String name) { 
     return request.getParameterValues(name); 
    } 

} 

do tego czasu, myślę, że wszystkie modyfikacje kod produkcyjny był bezpieczny. Teraz tworzę szew w moim pakiecie testowym. Jeśli dobrze zrozumiałem, teraz jestem w stanie bezpiecznie zmienić zachowanie szewu.Oto jak to zrobić:

public class FakeParameterSource implements ParameterSource { 

    public String[] values = {"ParamA","ParamB","ParamC"}; 

    public String[] getParameters(String name) { 
     return values; 
    } 
} 

I ostatni krok, byłoby uzyskać wsparcie od szwu, aby przetestować oryginalną behavoir metody.

import org.junit.Before; 
import org.junit.Test; 
import static org.junit.Assert.*; 
import static org.mockito.Mockito.*; 
import code.ABitOfLegacyRefactored; 
import static org.hamcrest.Matchers.*; 

public class ABitOfLegacySpecification { 

    private ABitOfLegacy aBitOfLegacy; 
    private String EMPTY = null; 

    @Before 
    public void initialize() { 
     aBitOfLegacy = new ABitOfLegacy(); 
    } 

    @Test 
    public void 
    the_output_gets_populated_when_the_request_is_not_empty 
    () { 
     FakeParameterSource fakeParameterSource = new FakeParameterSource(); 
     aBitOfLegacy.doSomeProcessing(fakeParameterSource); 
     assertThat(aBitOfLegacy.output,not(EMPTY)); 
    } 

    @Test(expected=NullPointerException.class) 
    public void 
    should_throw_an_exception_if_the_request_is_null 
    () { 
     aBitOfLegacy.doSomeProcessing(null); 
    } 
} 

To da mi 100% pokrycia testowego. Doceniam Twoje myśli:

  • Czy poprawnie zrzekłem zależności?
  • Czy w jednostce testowej czegoś brakuje?
  • Co można zrobić lepiej?
  • Czy ten przykład jest wystarczająco dobry, aby pomóc mi zrozumieć różnicę między szwem a makietą?
  • Jakim cudem może mi pomóc tutaj, jeśli nie używam szwu?
+0

Mocks nie zastępują szwów. – Gishu

+1

Powinieneś dostać tę książkę i przeczytać. –

Odpowiedz

4

Szwy

A seam jest miejscem, które pozwala modyfikować zachowanie bez modyfikowania kodu.

W twoim przykładzie poniższy przykład to szew obiektu (jeśli się nie mylę). Pozwala na przekazanie innego obiektu bez konieczności zmiany kodu. stąd jest to rodzaj szwu.

public void doSomeProcessing(ParameterSource request) {..} 

Poprzez wprowadzenie parametru jako typu abstrakcyjnego (zamiast konkretnej klasy) wprowadzono szew. Szew pozwala teraz modyfikować zachowanie metody bez edytowania jej kodu - tj. W miejscu wywołania można przekazać inny obiekt i uczynić metodę zrobić coś innego.

Mocks

Teraz zamiast tworzyć niestandardowe fałszywe (tworzenie podtyp interfejsu), można za pomocą makiety ramy zrobić coś takiego

Mocks również wsparcie twierdząc czy specyficzne metody były włączyłem go, dopasowując argumenty i inne fajne funkcje, które będą wykorzystywane przez testy. Mniej kodu testowego do utrzymania. MKSy są używane przede wszystkim do stwierdzenia, że ​​określone połączenie jest uzależnione. W twoim przykładzie wydaje się, że potrzebujesz Stubu, po prostu chcesz zwrócić wartość w puszkach.

Pardon mój zardzewiały JMock ..

@Test 
    public void 
    the_output_does_not_get_populated_when_the_request_is_empty 
    () { 
     Mockery context = new Mockery(); 
     final ParameterSource mockSource = context.mock(ParameterSource.class) 

context.checking(new Expectations(){{ 
    oneOf(mockSource).getParameters(); 
      will(returnValue(new string[]{"ParamA","ParamB","ParamC"}); 
}}); 
     aBitOfLegacy.populate(mockSource); 
     assertThat(aBitOfLegacy.output,not(EMPTY)); 
    } 

w .NET

var mockSource = new Mock<ParameterSource>(); 
mockSource.Setup(src => src.GetParameters()) 
      .Returns(new []{"ParamA","ParamB","ParamC"}); 
+0

Nadal jestem nieco zdezorientowany. Czy myślisz, że mój powyższy przykład można przetestować za pomocą mocks zamiast szwów? Dzięki nowoczesnym strukturom możemy ćwiczyć mocks, aby zachowywać się w określony sposób. Poza tym wykonujemy testy, czy szwy będą trudniejsze do utrzymania? – sfrj

+0

Ups - mój wpis nieco się pogmatwał. Zaktualizowany przykładem dla makiet ... może się nie kompilować - java nie jest dla mnie natywna. Aby użyć makiety, nadal potrzebujesz szwu - zwykle obiekt jako szew jako parametr lub argument. Nie ma "zamiast" - szew pozwala na zastąpienie innego obiektu (np. Do testowania). Mocks upraszczają testowanie interakcji. Oszczędzają wysiłek tworzenia niestandardowych fałszywych obiektów do testowania i utrzymywania ich. Zapewniają również lepsze raportowanie. – Gishu

+0

Rozumiem twoje wyjaśnienie. Dzięki za przykład testu jest miło :) Do tej pory nie rozumiałem znaczenia Szwy podczas testów jednostkowych. – sfrj

7

Szew jest miejscem w kodzie, który można włożyć do modyfikacji zachowań. Stworzyłeś szew podczas ustawiania zastrzyku zależności.

Jednym ze sposobów wykorzystania szwu jest wstawienie jakiegoś rodzaju podróbki. Fałki można ręcznie zwijać, jak w twoim przykładzie, lub tworzyć za pomocą narzędzia, takiego jak Mockito.

Tak, sztuczka jest rodzajem podróbki, a podróbka jest często używana przez wykorzystanie szwu.

Co do twoich testów i sposobu, w jaki zerwałeś zależność, to prawie tak bym to zrobił.

+0

Ładne wyjaśnienie, krótkie, ale dokładne. Dzięki. +1 – sfrj