2009-09-23 19 views
6

Varolan kodlarla çalışıyorum, buna eklemeye çalışıyorum ve bunun için birim testlerini artırıyorum. Ancak, kodun test edilebilir hale getirilmesiyle ilgili bazı sorunlara yol açıyor.Test Edilebilirlik için Tasarımcılar

Orjinal Oluşturucu:

public Info() throws Exception 
{ 
    _ServiceProperties = new ServiceProperties(); 
    _SshProperties = new SshProperties(); 
} 

bu kötü olduğunun farkında ve açıkçası test edilebilir değilim. Bir cemiyet ortamında, bu sınıf kendini inşa etmek için gerekli özellikleri bulamadığı için her seferinde yaratamaz. Şimdi, bu sınıfın bir parametre olarak 'yeni' ile başlayan herhangi bir şeyi hareket ettirmenin basit değişimi ile çok daha fazla test edilebilir olduğunun farkındayım.

Yeni Oluşturucu:

Yani ile bitirmek verir

public Info(ServiceProperties srvProps, SshProperties sshProps) throws Exception 
{ 
    _ServiceProperties = srvProps; 
    _SshProperties = sshProps; 
} 

beni düzgün birim test bu Bilgi sınıfını için. Sorun olsa, şimdi bütün bu işler başka sınıfa itilir geçerli:

Diğer Bazı Class' Yöntem:

public void useInfo() throws Exception 
{ 
    ServiceProperties srvProps = new ServiceProperties(); 
    SshProperties sshProps = new SshProperties(); 
    Info info = new Info(srvProprs, sshProprs); 
    doStuffWithInfo(info); 
} 

Şimdi bu yöntem test edilebilir değildir. Yaptığım tek şey, bu Mülkiyet nesnelerinin yapılarının meydana geldiği yerlere itmek ve başka bir yerde kodun aslında “yeni” olarak adlandırılmak zorunda kaldığı takılıp kalmasıdır.

İşte benim için ova: Bu "yeni" çağrıları başka bir yere basmakla ilgili bu olaylar zincirinin nasıl kırılacağını anlayamıyorum. Neyi kaçırıyorum?

cevap

7

Spring gibi Bağımlılık Enjeksiyon çerçevesine bakın. Kontrolün İnversiyonunun bu uygulaması, her türünüzün gördüğünüz tuzaktan kurtulabileceğini ve konfigürasyonu "kablo" bileşenleriyle birlikte bırakabileceğini gösterir.

Bu introduction to Spring (pdf), Spring hakkında genel bilgi verir. İlk bölüm veya iki kavramları açıklamak için yeterli olmalıdır.

Ayrıca Martin Fowler

+0

Ayrıca bir göz alabilir bizim yay ders materyali http://www.trainologic.org/courses/info/2 –

2

Doğru fikre sahipsiniz. Belki bu size yardımcı olacaktır. Tüm anlamlılık sınıflarınız için iki kuralı takip etmenizi öneririm, burada "anlamlılık", adımları izlemezseniz sınıfi test etmek, yeniden kullanmak veya sürdürmek daha zor olacaktır. İşte kurallar:

  1. Kural 1. bir başlangıç ​​var örneğini veya bağımlılık

arabirimleri için daima programı kendiliğinden elde asla. Artık bağımlılıklarını oluşturmak için Info sınıfınızı değiştirdiniz. "Bağımlılık" ile, diğer sınıflar, özellik dosyalarından veya yapılandırma dosyalarından yüklenen yapılandırma verileri kastediyorum.Bir şeyin nasıl yaratıldığına bağlı olduğunuzda, sınıfınızı ona bağlıyorsunuz ve bunu test etmek, yeniden kullanmak ve sürdürmek daha zorlaştırıyorsunuz. Yani, bir fabrika veya tek birtonla bir bağımlılık yaratılsa bile, sınıfınızın onu oluşturmasına izin vermeyin. Başka bir şey create() veya getInstance() ya da ne olursa olsun onu geçebilirsiniz.

Böylece sınıfınızı kullanan sınıf olarak "başka bir şey" seçtiniz ve bunun kötü bir koku olduğunu anladınız. Çare, bunun yerine başvurunuza giriş noktasının tüm bağımlılıkları başlatmasıdır. Geleneksel bir java uygulamasında, bu sizin main() yönteminizdir. Bunu düşünürseniz, sınıfları örneklendirmek ve onları birbirine bağlamak veya "birbirine bağlamak" mantığı özel bir mantıktır: "uygulama montajı" mantığı. Bu mantığı kodunuz boyunca yaymak mı yoksa daha kolay korumak için tek bir yerde toplamak daha mı iyi? Cevap, onu tek bir yerde toplamak daha iyidir - sadece bakım için değil, ama bunu yapma eylemi, tüm anlamlılık sınıflarınızı daha kullanışlı ve esnek bileşenlere dönüştürür.

main() ya da main() eşdeğerinizde, ihtiyacınız olan tüm nesneleri oluşturmalısınız, bunları birbirlerinin dizicileri ve yapıcılarına "kablolamak" için birbirine geçirmeniz gerekir. Ünite testleriniz daha sonra sahte nesneleri veya benzer şeyleri geçerek farklı şekillerde kablolar. Tüm bunları yapma eylemine "bağımlılık enjeksiyonu" denir. Dediğim gibi yaptıktan sonra büyük bir çirkin main() yöntemine sahip olacaksınız. Bu bir bağımlılık enjeksiyon aracının size yardımcı olabileceği ve aslında kodunuzu sonsuza kadar esnek hale getirebileceği yerdir. Bu noktaya geldiğinizde önereceğim araç, başkalarının da önerdiği gibi, Spring.

Daha az önemli olan kural # 2 - her zaman arayüzlere programlanır, çünkü uygulamadaki tüm bağımlılıkları ortadan kaldırır, yeniden kullanımı kolaylaştırarak, sahte nesne çerçeveleri, ORM çerçeveleri vb. de.

1

Spring, Guice, PicoContainer vb. Gibi bağımlılık önleme çerçevelerinin bile bir çeşit boostrap kullanması gerekir, böylece her zaman bir şeyler oluşturmanız gerekir.

Size yapılandırılmış bir sınıf örneği döndüren bir sağlayıcı/fabrika kullanmanızı öneririm. Bu, "yaratma" - hiyerarşiden çıkmanızı sağlar.

0

Başka bir sınıfın Bilgi sınıfı ve bağımlılıkları hakkında çok fazla bilgiye sahip olmasına izin verdiniz. Bir bağımlılık enjeksiyon çerçevesi bir sağlayıcı sınıfı kullanacaktır. Bu sahiptir

public void useInfo() throws Exception 
{ 
    Info info = infoProvider.get(); 
    doStuffWithInfo(info); 
} 

: En bazı-diğer sınıf onun kurucu bir Sağlayıcı <Bilgi> alırsak gibi yöntem olmazdı

interface Provider<T> { 
    T get(); 
} 

: Bir Bilgi nesnelerin Sağlayıcı yapabilirsiniz jenerik türlerini kullanma kodunuzdan beton sınıflarının inşaatını kaldırdınız. Bir sonraki adım, belirli bir birim-test durumu için bir alay yaratmayı kolaylaştırmak için bir ara yüze Bilgi sağlamaktır.

Ve evet, bu, tüm nesne yapım kodunu daha da ileriye doğru iter ve itecektir. Sadece bir şeyleri nasıl birbirine bağlayacağını açıklayan bazı modüllere yol açacaktır. Bu tür kodlar için "kurallar", koşullu ifadeler ve döngüler içermemesidir.

Misko Heverys blog okumanızı tavsiye ederim. Tüm sunumlar yararlıdır ve kuralları anladığınızı anlamanız yeterlidir.

1

Kişisel kurucular yanlış değildir ve sorun başka sözü herkesin ne hakkında, kod yürütüldüğünde zaman/nerede ilgili değil: Bağımlılık Enjeksiyon. Eğer nesne örneğini oluşturmak için sahte SshProperties nesneleri oluşturmanız gerekir.

public class MockSshProperties extends SshProperties { 
    // implemented methods 
} 

Sen Mockito gibi sahte çerçeveler kullanabilirsiniz: (son olarak işaretlenmemiş sınıf varsayarak) en basit yolu sınıfını uzatmaktır

public class Info { 

    private final sshProps; 
    private final serviceProps; 

    public Info() { 
     this(new SshProperties(), new ServiceProperties()); 
    } 

    public Info(SshProperties arg1, ServiceProperties arg2) { 
     this.sshProps = arg1; 
     this.serviceProps = arg2 
    } 
} 

public class InfoTester 
{ 
    private final static SshProperties sshProps = mock(SshProperties.class); 
    private final static ServiceProperties serviceProps = mock(ServiceProperties.class); 
    static { 
     when(sshProps.someGetMethod("something")).thenReturn("value"); 
    } 

    public static void main(String[] args) { 
     Info info = new Info(sshProps, serviceProps); 
     //do stuff 
    } 
} 
İlgili konular