Bunu yapmanın bir yolu olduğunu biliyorum:
@Test
public void foo(){
try{
//execute code that you expect not to throw Exceptions.
}
catch(Exception e){
fail("Should not have thrown any exception");
}
}
Bunu yapmanın daha temiz bir yolu var mı? (Muhtemelen Junit'in @Rule
özelliğini kullanarak?)
Bu konuya yanlış şekilde yaklaşıyorsunuz. Sadece işlevselliğinizi test edin: bir istisna atılırsa test otomatik olarak başarısız olacaktır. Herhangi bir istisna oluşmazsa, testlerinizin tümü yeşil renkte olacaktır.
Bu sorunun zaman zaman ilgi çektiğini fark ettim, bu yüzden biraz daha açacağım.
Birim testi yaparken, bir iş birimini ne olarak gördüğünüzü kendinize tanımlamanız önemlidir. Temel olarak: kod tabanınızın, tek bir işlevsellik parçasını temsil eden birden fazla yöntem veya sınıf içerebilen veya içermeyen bir çıkarımı.
Ya da The art of Unit Testing, 2. Baskı, Roy Osherove, sayfa 11'de tanımlandığı gibi:
gt; Bir birim testi, test edilen çalışma birimini çağıran ve ardından bu birimin tek bir sonucu hakkındaki bazı varsayımları kontrol eden otomatik bir kod parçasıdır. Bir birim testi neredeyse her zaman bir birim testi çerçevesi kullanılarak yazılır. Kolayca yazılabilir ve hızlıca çalıştırılabilir. Güvenilir, okunabilir ve bakımı yapılabilir. Üretim kodu değişmediği sürece sonuçlarında tutarlıdır.
Farkına varılması gereken şey, bir iş biriminin genellikle tek bir yöntem olmadığı, ancak en temel düzeyde bir yöntem olduğu ve bundan sonra diğer iş birimleri tarafından kapsüllendiğidir.
İdeal olarak, her ayrı iş birimi için bir test yönteminiz olmalıdır, böylece işlerin nerede yanlış gittiğini her zaman anında görebilirsiniz. Bu örnekte, bir kullanıcı döndürecek getUserById()
adında temel bir yöntem vardır ve toplam 3 iş birimi vardır.
İlk çalışma birimi, geçerli ve geçersiz girdi durumunda geçerli bir kullanıcının döndürülüp döndürülmediğini test etmelidir.
Veri kaynağı tarafından atılan tüm istisnalar burada ele alınmalıdır: kullanıcı yoksa, kullanıcı bulunamadığında bir istisna atıldığını gösteren bir test olmalıdır. Bunun bir örneği @Test(expected = IllegalArgumentException.class)
ek açıklamasıyla yakalanan IllegalArgumentException
olabilir.
Bu temel çalışma birimi için tüm kullanım durumlarınızı ele aldıktan sonra, bir seviye yukarı çıkarsınız. Burada da tamamen aynı şeyi yaparsınız, ancak yalnızca mevcut seviyenin hemen altındaki seviyeden gelen istisnaları ele alırsınız. Bu, test kodunuzun iyi yapılandırılmış olmasını sağlar ve işlerin nerede yanlış gittiğini bulmak için her yere atlamak yerine mimariyi hızlı bir şekilde çalıştırmanıza olanak tanır.
Bu noktada, bu istisnaları nasıl ele alacağımız açık olmalıdır. 2 tür girdi vardır: Geçerli girdi ve hatalı girdi (girdi tam anlamıyla geçerlidir, ancak doğru değildir).
Geçerli* girdilerle çalıştığınızda, yazdığınız testin çalışacağına dair örtük bir beklenti oluşturmuş olursunuz.
Böyle bir yöntem çağrısı aşağıdaki gibi görünebilir: existingUserById_ShouldReturn_UserObject
. Bu yöntem başarısız olursa (örneğin: bir istisna atılırsa), bir şeylerin yanlış gittiğini bilirsiniz ve araştırmaya başlayabilirsiniz.
Hatalı* girdiyi kullanan ve bir istisna bekleyen başka bir test (nonExistingUserById_ShouldThrow_IllegalArgumentException
) ekleyerek, yönteminizin yanlış girdi ile yapması gerekeni yapıp yapmadığını görebilirsiniz.
Testinizde iki şey yapmaya çalışıyordunuz: geçerli ve hatalı girdiyi kontrol etmek. Bunu her biri tek bir şey yapan iki yönteme böldüğünüzde, çok daha net testlere ve işlerin nerede yanlış gittiğine dair çok daha iyi bir genel bakışa sahip olacaksınız.
Katmanlı çalışma birimini aklınızda tutarak, hiyerarşide daha yüksek bir katman için ihtiyacınız olan test miktarını da azaltabilirsiniz çünkü alt katmanlarda yanlış gitmiş olabilecek her şeyi hesaba katmak zorunda değilsiniz: mevcut katmanın altındaki katmanlar, bağımlılıklarınızın çalıştığının sanal bir garantisidir ve bir şeyler ters giderse, mevcut katmanınızdadır (alt katmanların kendilerinin herhangi bir hata vermediğini varsayarak).
Eğer kodunuzdaki tüm hataları yakalayamayacak kadar şanssızsanız. Aptalca yapabilirsin
class DumpTest {
Exception ex;
@Test
public void testWhatEver() {
try {
thisShouldThroughError();
} catch (Exception e) {
ex = e;
}
assertEquals(null,ex);
}
}
Test hedefinizin istisnayı tüketip tüketmediğini test etmek istiyorsanız. Testi (jMock2 kullanarak mock collaborator) olarak bırakmanız yeterlidir:
@Test
public void consumesAndLogsExceptions() throws Exception {
context.checking(new Expectations() {
{
oneOf(collaborator).doSth();
will(throwException(new NullPointerException()));
}
});
target.doSth();
}
Hedefiniz fırlatılan istisnayı tüketirse test başarılı olur, aksi takdirde test başarısız olur.
İstisna tüketim mantığınızı test etmek istiyorsanız, işler daha karmaşık hale gelir. Tüketimi, taklit edilebilecek bir işbirlikçiye devretmenizi öneririm. Bu nedenle test şöyle olabilir:
@Test
public void consumesAndLogsExceptions() throws Exception {
Exception e = new NullPointerException();
context.checking(new Expectations() {
{
allowing(collaborator).doSth();
will(throwException(e));
oneOf(consumer).consume(e);
}
});
target.doSth();
}
Ancak bazen sadece günlüğe kaydetmek istiyorsanız aşırı tasarlanmış olabilir. Bu durumda, bu makale (http://java.dzone.com/articles/monitoring-declarative-transac, http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there-an-easy-way/) bu durumda tdd'de ısrar ederseniz yardımcı olabilir.