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?
Mocks nie zastępują szwów. – Gishu
Powinieneś dostać tę książkę i przeczytać. –