İşte benim yaklaşımım. Zaman açısından bir maliyeti var çünkü 4 aşamada bir yeniden test edicidir.
Açıklayacağım şey, sorunun örneğinde açıklanandan daha karmaşık olan bileşenlerde daha iyi olabilir.
Her halükarda, strateji, herhangi bir bileşen adayının bir arayüz tarafından normalleştirilmesi için geçerlidir (DAO, Servisler, Kontrolörler, ...).
1. arayüz
Tüm genel yöntemleri MyDocumentService'ten toplayalım ve hepsini bir araya getirelim . Örneğin. Zaten varsa, yenisini ayarlamak yerine onu kullanın .
public interface DocumentService {
List<Document> getAllDocuments();
//more methods here...
}
Sonra MyDocumentService'i bu yeni arayüzü uygulamaya zorluyoruz .
Çok uzak çok iyi. Önemli bir değişiklik yapılmadı, mevcut sözleşmeye saygı gösterdik ve davranışlar dokunulmadı.
public class MyDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//legacy code here as it is.
// with no changes ...
}
}
2. Eski kod Birim testi
İşte zor işimiz var. Bir test takımı kurmak için. Mümkün olduğu kadar çok vaka belirlemeliyiz: başarılı vakalar ve ayrıca hata davaları. Bu son, sonucun kalitesinin iyiliği içindir.
Şimdi, MyDocumentService'i test etmek yerine arayüzü test edilecek sözleşme olarak kullanacağız.
Ayrıntılara girmeyeceğim, bu yüzden beni affet. Kodum çok basit veya çok agnostik görünüyorsa
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
}
}
Bu aşama, bu yaklaşımda diğerlerinden daha uzun sürer. Ve en önemlisi çünkü gelecekteki karşılaştırmalar için referans noktası belirleyecektir.
Not: Büyük değişiklikler yapılmamasından ve davranışçıya dokunulmadan kalmasından dolayı. Burada SCM'ye bir etiket yapmayı öneririm. Etiket veya dal önemli değil. Sadece bir versiyon yap.
Geri almalar, sürüm karşılaştırmaları için istiyoruz ve eski kodun ve yenisinin paralel yürütülmesi için olabilir.
3. Yeniden düzenleme
Refactor yeni bir bileşene uygulanacak. Mevcut kodda herhangi bir değişiklik yapmayız. İlk adım MyDocumentService’in kopyalayıp yapıştırmak ve CustomDocumentService olarak yeniden adlandırmak kadar kolaydır (örneğin).
Yeni sınıf, DocumentService'i uygulamaya devam ediyor . Sonra gidip getAllDocuments () işlevini yeniden düzenleyin . (Hadi başlayalım. Pin-refactors)
DAO'nun arayüzünde / yöntemlerinde bazı değişiklikler gerektirebilir. Eğer öyleyse, mevcut kodu değiştirmeyin. DAO arayüzünde kendi metodunuzu kullanın. Eski kodu Kaldırıldı olarak not edin ve daha sonra kaldırılması gerekenleri bileceksiniz.
Mevcut uygulamayı bozmamak / değiştirmemek önemlidir. Her iki hizmeti de paralel olarak yürütmek ve sonuçları karşılaştırmak istiyoruz.
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. DocumentServiceTestSuite'ın güncellenmesi
Tamam, şimdi daha kolay olanı. Yeni bileşenin testlerini eklemek.
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);
}
}
Artık her ikisi de bağımsız olarak doğrulanmış oldResult ve newResult değerlerine sahibiz, ancak birbirleriyle de karşılaştırabiliriz. Bu son doğrulama isteğe bağlıdır ve sonuca bağlıdır. Bu karşılaştırılabilir değil olabilir.
İki koleksiyonun bu şekilde karşılaştırılması çok fazla duyarlı olmayabilir, ancak başka herhangi bir nesne için geçerli olur (pojos, veri modeli varlıkları, DTO'lar, Sarmalayıcılar, yerel türler ...)
notlar
Birim testleri yapmayı ya da sahte kütüphaneleri nasıl kullanacağımı söylemeye cesaret edemem. İkisini de nasıl refactor yapmak zorunda olduğunu söylemeye cesaret edemem. Yapmak istediğim küresel bir strateji önermek. Nasıl ileri götürüleceğiniz size bağlıdır. Tam olarak kodun ne olduğunu, karmaşıklığını ve bu tür bir stratejinin denemeye değer olduğunu biliyorsunuz. Zaman ve kaynaklar gibi gerçekler burada önemlidir. Ayrıca gelecekte bu testlerden ne beklediğiniz önemlidir.
Örneklerimi bir Servis tarafından başlattım ve DAO ile takip ediyorum. Bağımlılık seviyelerine derinlemesine gitmek. Aşağı yukarı strateji olarak tanımlanabilir . Ancak küçük değişiklikler / refactors ( tur örneğinde maruz kalan gibi ) için aşağıdan yukarıya bir iş daha kolay olacaktır. Çünkü değişikliklerin kapsamı azdır.
Son olarak, kullanımdan kaldırılmış kodu kaldırmak ve eski bağımlılıkları yenisine yönlendirmek size kalmıştır.
Kaldırılan testleri de kaldırın ve işiniz bitti. Eski çözümü testleriyle birlikte sürümlendirdiyseniz, istediğiniz zaman birbirinizi kontrol edip karşılaştırabilirsiniz.
Bu kadar çok çalışmanın sonucunda test edilmiş, onaylanmış ve versiyonlanmış eski kodlara sahipsiniz. Ve yeni kod, test edildi, onaylandı ve versiyonlanmaya hazır.