Oto moje podejście. Ma koszt pod względem czasu, ponieważ jest to test refaktorski w 4 fazach.
To, co zamierzam ujawnić, może lepiej pasować do komponentów o większej złożoności niż ta przedstawiona w przykładzie pytania.
W każdym razie strategia jest ważna dla dowolnego kandydata na komponent, który ma zostać znormalizowany przez interfejs (DAO, usługi, kontrolery, ...).
1. Interfejs
Zbierzmy wszystkie publiczne metody z MyDocumentService i połączmy je wszystkie w jeden interfejs. Na przykład. Jeśli już istnieje, użyj tego zamiast ustawiania nowego .
public interface DocumentService {
List<Document> getAllDocuments();
//more methods here...
}
Następnie zmuszamy MyDocumentService do wdrożenia tego nowego interfejsu.
Na razie w porządku. Nie wprowadzono większych zmian, dotrzymaliśmy aktualnego kontraktu, a behaivos pozostaje nietknięte.
public class MyDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//legacy code here as it is.
// with no changes ...
}
}
2. Test jednostkowy starszego kodu
Tutaj mamy ciężką pracę. Aby skonfigurować pakiet testowy. Powinniśmy ustawić jak najwięcej przypadków: przypadki udane, a także przypadki błędów. Te ostatnie służą dobrej jakości wyniku.
Teraz zamiast testować MyDocumentService , będziemy używać interfejsu jako testowanej umowy.
Nie będę wchodził w szczegóły, więc wybacz mi, jeśli mój kod wygląda na zbyt prosty lub zbyt agnostyczny
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
//... More mocks
DocumentService service;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
//this is purposed way to inject
//dependencies. Replace it with one you like more.
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> result = service.getAllDocuments();
Assert.assertX(result);
Assert.assertY(result);
//... As many you think appropiate
}
}
Ten etap trwa dłużej niż jakikolwiek inny w tym podejściu. I to jest najważniejsze, ponieważ wyznaczy punkt odniesienia dla przyszłych porównań.
Uwaga: Z powodu braku większych zmian, zachowanie nie zostało zmienione. Proponuję zrobić tag tutaj w SCM. Tag lub gałąź nie ma znaczenia. Po prostu zrób wersję.
Chcemy go do wycofywania, porównywania wersji i może być do równoległego wykonywania starego kodu i nowego.
3. Refaktoryzacja
Refactor zostanie wdrożony w nowym komponencie. Nie dokonamy żadnych zmian w istniejącym kodzie. Pierwszy krok jest tak prosty, jak skopiowanie i wklejenie MyDocumentService i zmiana nazwy na CustomDocumentService (na przykład).
Nowa klasa nadal wdraża usługę DocumentService . Następnie przejdź i refaktoryzuj getAllDocuments () . (Zacznijmy od jednego. Refaktory)
Może to wymagać pewnych zmian w interfejsie / metodach DAO. Jeśli tak, nie zmieniaj istniejącego kodu. Zaimplementuj własną metodę w interfejsie DAO. Oznacz stary kod jako Przestarzały, a później dowiesz się, co należy usunąć.
Ważne jest, aby nie przerywać / zmieniać istniejącej implementacji. Chcemy wykonywać obie usługi równolegle, a następnie porównywać wyniki.
public class CustomDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//new code here ...
//due to im refactoring service
//I do the less changes possible on its dependencies (DAO).
//these changes will come later
//and they will have their own tests
}
}
4. Aktualizacja DocumentServiceTestSuite
Ok, teraz łatwiejsza część. Aby dodać testy nowego komponentu.
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
DocumentService service;
DocumentService customService;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
customService = CustomDocumentService(mockDepA, mockDepB);
// this is purposed way to inject
//dependencies. Replace it with the one you like more
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> oldResult = service.getAllDocuments();
Assert.assertX(oldResult);
Assert.assertY(oldResult);
//... As many you think appropiate
List<Document> newResult = customService.getAllDocuments();
Assert.assertX(newResult);
Assert.assertY(newResult);
//... The very same made to oldResult
//this is optional
Assert.assertEquals(oldResult,newResult);
}
}
Teraz mamy zarówno oldResult, jak i newResult, które zostały sprawdzone niezależnie, ale możemy je również porównać ze sobą. Ta ostatnia walidacja jest opcjonalna i zależy od wyniku. Być może nie jest to porównywalne.
Może nie robić zbyt wiele sensu, aby porównywać dwie kolekcje w ten sposób, ale byłoby ważne dla każdego innego rodzaju obiektu (pojos, encje modelu danych, DTO, opakowania, typy rodzime ...)
Notatki
Nie odważyłbym się powiedzieć, jak przeprowadzać testy jednostkowe ani jak używać fałszywych bibliotek. Nie mam odwagi powiedzieć, jak trzeba zrobić refaktor. Chciałem zaproponować globalną strategię. To, jak pójść naprzód, zależy od Ciebie. Wiesz dokładnie, jaki jest kod, jego złożoność i czy taka strategia jest warta wypróbowania. Liczą się tu fakty, takie jak czas i zasoby. Ważne jest również to, czego oczekujesz od tych testów w przyszłości.
Zacząłem moje przykłady od usługi i podążałem za DAO i tak dalej. Wchodzenie głęboko w poziomy zależności. Mniej więcej to może być opisany jako góra-dolnym strategii. Jednak w przypadku drobnych zmian / refaktorów ( takich jak ten pokazany w przykładzie trasy ) oddolne wykonanie zadania byłoby łatwiejsze. Ponieważ zakres zmian jest niewielki.
Wreszcie, to do Ciebie należy usunięcie przestarzałego kodu i przekierowanie starych zależności na nowe.
Usuń również przestarzałe testy i zadanie zostanie wykonane. Jeśli wersjonujesz stare rozwiązanie z jego testami, możesz sprawdzić i porównać się w dowolnym momencie.
W wyniku tak dużej ilości pracy przetestowano, sprawdzono i zaktualizowano starszy kod. Nowy kod, przetestowany, sprawdzony i gotowy do wersji.