Söz konusu sınıfı bölmelisiniz.
Her sınıf bazı basit işler yapmalıdır. Göreviniz test edemeyecek kadar karmaşıksa, sınıfın yaptığı görev çok büyük demektir.
Bu tasarımın aptallığını gözardı etmek:
class NewYork
{
decimal GetRevenue();
decimal GetExpenses();
decimal GetProfit();
}
class Miami
{
decimal GetRevenue();
decimal GetExpenses();
decimal GetProfit();
}
class MyProfit
{
MyProfit(NewYork new_york, Miami miami);
boolean bothProfitable();
}
GÜNCELLEŞTİRME
Bir sınıftaki saplama yöntemleriyle ilgili sorun, kapsüllemeyi ihlal etmenizdir. Testiniz, nesnenin dış davranışının spesifikasyonlara uygun olup olmadığını kontrol etmelidir. Nesnenin içinde ne olursa olsun onun işi değil.
FullName'in FirstName ve LastName kullanıyor olması bir uygulama detayıdır. Sınıfın dışında hiçbir şey bunun gerçek olduğunu umursamalıdır. Nesneyi test etmek için genel yöntemlerle alay ederek, o nesnenin uygulandığına dair bir varsayımda bulunuyorsunuz.
Gelecekte bir noktada, bu varsayımın doğru olması sona erebilir. Belki de tüm ad mantığı, Kişinin basitçe çağırdığı bir Ad nesnesine taşınacaktır. Belki de FullName üye değişkenlerine first_name ve last_name 'e doğrudan erişmek yerine FirstName ve LastName' i çağırır.
İkinci soru, neden böyle yapmak zorunda hissettiğinizdir. Tüm sınıfınız bittikten sonra şöyle bir şey test edilebilir:
Person person = new Person("John", "Doe");
Test.AssertEquals(person.FullName(), "John Doe");
Bu örnek için herhangi bir şeyi saplamaya ihtiyaç duymamalısınız. Eğer yaparsan o zaman çok mutlu ve iyisin ... kes şunu! Buradaki yöntemlerle alay etmenin bir faydası yok çünkü zaten içlerinde ne olduğu konusunda kontrolü elinizde tutuyorsunuz.
FullName'in alay etmek için kullandığı yöntemlerin mantıklı geldiği tek durum, bir şekilde FirstName () ve LastName () 'nin önemsiz işlemler olup olmadığıdır. Belki de bu rasgele isim üreticilerinden birini yazıyorsunuzdur ya da FirstName ve LastName, cevap için veritabanını sorguluyor. Fakat eğer gerçekleşen buysa, nesnenin Person sınıfına ait olmayan bir şey yaptığını gösterir.
Başka bir deyişle, yöntemleri alay etmek, nesneyi almak ve iki parçaya bölmektir. Diğer parça test edilirken bir parça alay ediliyor. Yaptığınız şey, esasen nesnenin ayrılmasından kaynaklanan geçici bir durumdur. Bu durumda, nesneyi zaten ayırın.
Sınıfınız basitse, bir test sırasında bununla alay etme gereğini hissetmemelisiniz. Eğer sınıfınız alay etmek zorunda olduğunuzu hissedecek kadar karmaşıksa, sınıfı daha basit parçalara bölmelisiniz.
TEKRAR GÜNCELLEME
Gördüğüm gibi, bir nesnenin dış ve iç davranışları vardır. Dış davranış, başka nesnelere çağrılar gibi değerler döndürür. Açıkçası, bu kategorideki herhangi bir şey test edilmelidir. (aksi halde neyi test ederdiniz?) Fakat içsel davranış gerçekten test edilmemelidir.
Şimdi iç davranış test edilir, çünkü dış davranışla sonuçlanan budur. Ancak testleri doğrudan içsel davranış üzerine yazmam, sadece dolaylı olarak dış davranış üzerinden.
Bir şeyi test etmek istersem, dış davranışa dönüşmesi için taşınması gerektiğini düşünüyorum. Bu nedenle, bir şeyle alay etmek istiyorsanız, alay etmek istediğiniz şeyin şimdi söz konusu nesnelerin dış davranışında olması için nesneyi ayırmanız gerekir.
Ancak, ne fark eder? FirstName () ve LastName () başka bir nesnenin üyeleriyse, gerçekten FullName () sorununu değiştirir mi? FirstName ile alay etmenin gerekli olduğuna karar verirsek ve LastName aslında başka bir nesnede olmalarına yardımcı olur mu?
Bence alaycı yaklaşımını kullanırsan, nesnede bir dikiş oluşturursun. Doğrudan bir dış veri kaynağıyla iletişim kuran FirstName () ve LastName () gibi işlevleriniz var. Sizde olmayan FullName () de var. Ancak hepsi aynı olmadığı için açık değildir. Bazı parçaların veri kaynağına doğrudan erişmesi gerekmiyor, diğerleri ise. Bu iki grubu dağıtırsanız, kodunuz daha net olacaktır.
DÜZENLE
Geri bir adım atalım ve şunu soralım: Test ederken neden nesnelerle alay ediyoruz?
- Testlerin tutarlı bir şekilde çalışmasını sağlayın (çalışmadan koşuya değişen şeylere erişmekten kaçının)
- Pahalı kaynaklara erişmekten kaçının (üçüncü taraf hizmetlerine vb. Vurmayın).
- Test edilen sistemi basitleştirin
- Tüm olası senaryoları test etmeyi kolaylaştırın (örn. Başarısızlık simülasyonu vb.)
- Diğer kod parçalarının ayrıntılarına bağlı kalmaktan kaçının, böylece diğer kod parçalarındaki değişiklikler bu testi bozmaz.
Şimdi, neden 1-4'ün bu senaryo için geçerli olmadığını düşünüyorum. Tam adı sınarken harici kaynağın alay edilmesi, alay etmenin tüm bu nedenleriyle ilgilenir. Ele alınmayan tek parça basitliktir, ancak nesnenin bir endişe olmayan yeterince basit olduğu anlaşılmaktadır.
Endişenizin 5 numaralı sebep olduğunu düşünüyorum. Endişe verici, bir noktada FirstName ve LastName'in uygulanmasının değiştirilmesinin bir noktada testi geçeceği yönünde. Gelecekte, FirstName ve LastName, adları farklı bir konumdan veya kaynaktan alabilir. Fakat FullName muhtemelen her zaman olacaktır FirstName() + " " + LastName()
. Bu nedenle, FirstName ve LastName ile alay ederek FullName'i test etmek istiyorsunuz.
O zaman sahip olduğunuz kişi nesnesinin, diğerlerinden daha büyük olasılıkla değişmesi muhtemel alt kümesidir. Nesnenin geri kalanı bu alt kümeyi kullanır. Bu alt küme şu anda bir kaynağı kullanarak verilerini alıyor, ancak daha sonraki bir tarihte bu verileri tamamen farklı bir şekilde alabilir. Ama bana göre bu alt küme dışarı çıkmaya çalışan ayrı bir nesne gibi geliyor.
Bana öyle geliyor ki, eğer nesnenin yöntemiyle alay ediyorsanız, nesneyi ayırıyorsunuz. Ama bunu geçici olarak yapıyorsun. Kodunuz Person nesnesinin içinde iki ayrı parça bulunduğunu açıkça belirtmiyor. Bu yüzden basitçe o nesneyi gerçek kodunuzda bölün, böylece kodun ne olup bittiğini okumasından açıkça anlaşılır. Mantıklı olan ve her test için nesneyi farklı şekilde bölmeye çalışmayan nesnenin gerçek bölmesini seçin.
Nesnenizi bölmek için itirazda bulunabileceğinizden şüpheleniyorum, ama neden?
DÜZENLE
Ben hatalıydım.
Bireysel yöntemlerle alay ederek geçici bölmeler eklemekten ziyade nesneleri bölmelisiniz. Ancak, nesneleri bölmenin bir yöntemine fazlasıyla odaklandım. Ancak, OO, bir nesneyi bölmek için birden çok yöntem sağlar.
Ne önereceğim:
class PersonBase
{
abstract sring FirstName();
abstract string LastName();
string FullName()
{
return FirstName() + " " + LastName();
}
}
class Person extends PersonBase
{
string FirstName();
string LastName();
}
class FakePerson extends PersonBase
{
void setFirstName(string);
void setLastName(string);
string getFirstName();
string getLastName();
}
Belki de başından beri yaptığın şey budur. Fakat bu yöntemin alaycı yöntemlerle gördüğüm sorunlara sahip olacağını düşünmüyorum çünkü her yöntemin hangi tarafında olduğunu açıkça belirttik. Ve devralma kullanarak, ek bir sarmalayıcı nesnesi kullanırsak ortaya çıkacak beceriksizliği önleriz.
Bu, biraz karmaşıklık getirmektedir ve sadece birkaç yardımcı fonksiyon için, temelde 3. parti kaynağını alay ederek muhtemelen onları test edeceğim. Elbette, kırılma tehlikesi artar ancak yeniden düzenlenmeye değmez. Eğer ayırmanız gereken yeterince karmaşık bir nesneye sahipseniz, bunun gibi bir şeyin iyi bir fikir olduğunu düşünüyorum.