Java ile Test 101

Burak Acar
6 min readJan 10, 2021

--

Neden test ederiz?

Yazılım kullanıma sunulmadan, kullanım durumunda oluşabilecek hataları tespit etmek, yazılımı daha performanslı bir hale getirmek için testler gerçekleştirilir.

Test işlemleri yazılımın kalitesini destekleyen bir argüman ortaya koyar. Test oranı yüksek olan bir yazılımda hata çıkma ihtimali daha düşüktür. Yazılımın belirlenen gereksinimleri karşıladığı test edilir. Farklı platformlarda kullanılacak bir yazılımı kullanılacağı her platform için ayrı ayrı test ederek sorunsuz çoklu ortam desteği verilebilir.

Yazılımların %100 test edilmesi zaman/maliyet bakımından zordur ancak kritik alanların test edilmesi, oluşabilecek hataların tespit edilmesini ve çözülmesini daha kolay bir hale getirir.

Test aşamaları

Test aşamaları yazılıma göre değişiklik gösterebilir.

1- Integration Testing (Entegrasyon Testi)

Entegrasyon testi veya Genel test birbiri ile bağlı olarak çalışacak modüllerin test edildiği aşamadır. Modüllerin içerisinde olan hataları değil beraber çalışma durumlarını çözmek için yapılan testlerdir. Modüllerin birbirlerinden ayrı şekilde hatasız çalışma durumu Unit test aşamasında kontrol edilmelidir. Toplam testlerin %20'si entegrasyon testleridir.

2- Regression Testing (Regresyon Testi)

Yazılımın ilerleyen adımlarında yapılan değişikliklerin geriye dönük bir probleme sebep olmadığının kontrolü bu aşamada sağlanır. Çevik geliştirme içerisinde yapılan değişiklikler, önceden yazılmış ve testlerden başarılı sonuç almış adımları etkileyebilir, regrasyon testi önceden yazılan tüm testleri geriye dönüp çalıştırarak oluşabilecek sorunların önüne geçmemizi sağlar.

3-UI Testing (Arayüz Testleri)

Yazılım geliştirimi sırasında manuel olarak yazılımın ara yüzlerini test ederiz. Ui test aşamasında, bu manuel işlemleri otomasyon haline getirmenin, sonuçları otomatik elde etmenin sağlandığı bir test adımıdır. Günümüzde çoğu yazılımda bu işlem maliyet ve zaman kaygılarından dolayı yapılmıyor ve manuel arayüz testleri ile ilerleniyor.

4-Performance Testing (Performans Testleri)

Performans testi, yazılımın belirlenen iş yükü altında performansının ölçüldüğü adımdır. Bu testler sayesinde, yazılımın yol açabileceği trafik öngörülebilir, yazılım kullanıcıyla buluşmadan performans sorunları ortadan kaldırılabilir.

5-User Acceptance Testing (Kullanıcı Kabul Testleri)

Çevik yaklaşımlarla birlikte, kullanıcı kabul testine gelene kadar kullanıcıların yazılımın geliştirilme aşamalarına müdahaleleri olmakta ancak uat adımı yazılımın anlaşılan gereksinimleri karşılayıp karşılamadığının kontrolünün sağlandığı adımdır.

6-Unit Testting (Birim Testleri)

Yazılımın en küçük parçalarının tek tek ve birbirinden bağımsız şekilde test edilmesidir. Yazılıma doküman sağlaması açısından da önemli bir adımdır. Geliştirilen yazılımda ileride oluşabilecek sorunların veya yazılım takımında oluşabilecek değişikliklerin sonrasında testler üzerinden yazılımın anlaşılması çok daha kolaydır. Yazılan kodun doğrudan test edilmesi ile beraber hızlı bir şekilde düzenlemeler yapılmasına da olanak sağlar. Unit test adımıyla ilgili en önemli husus, testlerin parçaların çalıştığını kontrol etmesidir, tüm testlerin başarılı olması tümüyle sistemin düzgün çalışacağı anlamına gelmez.

Unit test için kritik bir hususta her olası durum için ayrı test adımının çalıştırılmasıdır çünkü birden fazla senaryo için bir test yazılırsa hangi senaryoda sorun olduğunun tespit edilmesi ek bir zaman maliyeti getirecektir.

Testlerin tam otomatik şekilde çalışması ve kolay anlaşılacak şekilde yazılması kritiktir, bu durumlar için testin isimlendirilmesi ve içerisinde yazılan kodun sadeliği önemlidir.

Unit test yazarken önce teste hazırlık olarak bilinen adımlar yapılır, sonrasında test edilecek işlemin çalıştırıldığı bir adım vardır ve son olarak sonucun test doğruluğunun kontrol edildiği bir adım yer alır, bu sıralama given-when-then olarak karşımıza çıkar.

Toplama işlemi yapan kodun test aşamaları

@Test anotasyonu junit ile gelen bir yapıdır ve yazılan kodun testin gerçekleştiği adım olduğunu belirtir. Assert equals ile fonksiyon içerisinden dönen değerin, olması gereken değer olup olmama durumu kontrol edilir, bu test başarısız olacaktır. Toplama işleminden 24 sonucu döner ve bunun 23 ile kontrolü sağlanır. Değerler eşit olmadığından test işlemi fonksiyonun hatalı olduğunu belirtir.

Unit Test Yazma Kuralları

1- Her işlem için bir test yazılıp, her testin ayrı ayrı çalıştırılması tavsiye edilir.

Örneğin bir veri tabanına insert işlemi ve delete işlemi içeren iki farklı fonksiyonunuz var. İnsert işleminde problem oluşması durumunda delete işlemi içeren fonksiyon test edilmeden hatalı gözükebilir. Bu gibi durumların oluşumunun önüne geçmek için farklı işlemleri aynı anda test etmek tavsiye edilmez.

2- Bir test fonksiyonunu kendi içinde bölümlere ayırmak yazılımın okunabilirliğini arttırır.

a. Given: Test edilecek işlemden önce yapılması gerekli olan işlemlerin yapıldığı adımdır, teste hazırlık olarak düşünebiliriz.

b. When: Asıl işlemin çalıştırıldığı adımdır, testin yapıldığı adımdır.

c. Then: Bu aşamasında test ettiğimiz işlemin değerinin kontrol edildiği adımdır. Testin doğru veya yanlış olma durumu bu adımda belli olur.

3- Test fonksiyonu çalıştırıldığında dışarıdan ek bir gereksinim ihtiyacı duymamalıdır.

Örneğin, 1. Maddede verdiğimiz örnekte veri tabanına insert etmek için bir bağlantı yapısının ve Oracle Database, MsSql, MySql gibi bir veri tabanı olması gereklidir. Yazılan işlem doğru olsa bile dış etmenlerden dolayı test olumsuz sonuçlanabilir. Bu durumun önüne geçmek için dış bağlantıları sanallaştırmak gibi yöntemler kullanılır. Bu işlemi sağlayan bir çok üçüncü parti yazılım bulunur.(Mockito)

4- Bir test işlemi yapmak için başka bir test işlemi yapılmak zorunda kalınmamalıdır.

Örneğin veri tabanından silme işlemini test etmek için önce ekleme yapan fonksiyonun çağırılmasına gerek olmamalıdır.

5- Test fonksiyonlarını isimlendirmek için yazılımcıların kullandığı birçok farklı yöntem vardır. Bu tarz kullanımlar yapmak test fonksiyonunun ne iş yaptığını daha anlaşılır hale getirmek ve belgelendirme açısından faydalıdır. Bazı kullanım şekilleri aşağıdaki gibidir.

a. MethodName_StateUnderTest_ExpectedBehavior

b. MethodName_ExpectedBehavior_StateUnderTest

c. test[Feature being tested]

d. Feature to be tested

e. Should_ExpectedBehavior_When_StateUnderTest

f. When_StateUnderTest_Expect_ExpectedBehavior

g. Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior

Unit Test Türleri

Birim test yapmanın 2 farklı yolu bulunuyor:

1- Manuel Test: Test senaryolarının herhangi bir araç desteği olmadan manuel olarak yürüterek yapılan testlerdir. Bu tür daha fazla zaman tüketimine neden olur ve daha az güvenlidir.

2- Otomatik Test: Test senaryolarının yardımcı araçlar desteğiyle yürütüldüğü adımdır. Hızlı ve daha güvenilirdir.

Junit için kullanılan bazı anotasyonlar

@Test — Test işleminin gerçekleştiğini belirtir.

@Test (timeout = 1000) — Test anotasyonu bu şekilde bir ek yapıyla beraber kullanılabilir. Bu durum testin doğru çalışsa bile 1 saniyeden uzun sürmesi durumunda başarısız sonuçlanacağını belirtir.

@BeforeClass — Bir class içerisinde birçok test fonksiyonu yazılabilir. BeforeClass ile birlikte testlerin hiçbirisi çalıştırılmadan yapılacak işlemleri belirtebiliriz. Yani sınıfın içinde ilk çalıştırılan yapıdır ve sadece 1 kez çalışır.

@Before — Bir test clasında yazılan tüm test işlemleri için işlem yapılmadan önce ortam hazırlanmak için kullanılan bir yapıdır. Her test işlemi öncesi tetiklenir. Mesela bir ekleme işlemi testi yapacaksanız db bağlantılarını, silme testi yapacaksanız ilk ekleme işlemini bu yapıyla sağlayabilirsiniz.

@After — Her test adımından sonra çalışan adımdır. Örneğin bir ekleme işlemi test ediyorsanız ve after işleminde yaptığınız değişiklikleri test öncesi haline geri getirebilirsiniz. Silme işleminde, after kısmında silinen datayı ekleyebilirsiniz. Bu aşama sonradan çalışacak test adımlarına düzgün bir yapı bırakmamızda önemli bir rol oynar.

@Ignore — Test sınıfı içerisinde olan testleri grup halinde çalıştırdığımızda çalışmasını istemediğimiz fonksiyonları bu şekilde belirtebiliriz. Ignore olarak belirtilen kısım dahil edilmez.

@AfterClass — Tüm test işlemleri bittikten sonra çalışacak kısmı belirtir.

Anotasyonları ve çalışma sıralarını örnek üzerinden inceleyelim.

Örnek içerisinde kullanılan assert kısımları sonuç kontrolü sağlamak için kullanılan yapılardır. FindMax, Cube ve ReverseWord isimli fonksiyonlar için yazılan testler görülmektedir.

İlk olarak test işlemlerinin hepsinden önce @BeforeClass kullanılan fonksiyon çalışır, daha sonra @Before, @Test, @After sıralamasıyla tüm test işlemleri çalışır. Tüm test fonksiyonları çalışmasını tamamladıktan sonra @AfterClass olarak tanımlanan fonksiyon çalışır ve test işlemleri sonlanır.

JUnit Assert Sınıfı

Assert sınıfı temel olarak program çıktısının doğruluğunun kontrolü için kullanılan yapılar sunar.

1- void assertEquals(boolean expected,boolean actual) — Bu kontrol java’nın object sınfında bulunan equals yapısına benzer şekilde çalışır, kendi objelerimizi kıyaslamak istersek equels fonksiyonunu ezip kendi sınıfımıza özel yazmış (override) olmamızı bekler, yoksa object sınıfının içinden kullanarak hatalı dönüş sağlar. Equals fonksiyonu override etmiş objelerin kıyaslanması için kullanılabilir.

2- void assertTrue(boolean condition): Bir koşulun doğru olup olmadığını kontrol eder

3- void assertFalse(boolean condition): Bir koşulun yanlış olup olmadığını kontrol eder. Önemli olan kısım, koşul False ise testi True olur.

4- void assertNull(Object obj): Verilen objenin Null(Boş) olma durumunu kontrol eder.

5- void assertNotNull(Object obj): Verilen değerin Null olma durumunu kontrol eder, değer null değilse sonuç başarılıdır.

Bu 5 kontrole ek olarak birçok farklı kontrol yapısı bulunmaktadır. Yukarıda bulunan kodda assertEquals işleminin örnek kullanımı görülmektedir.

AssertJ ile Java’da İstisnaları(Exception) Test Etme

İlk olarak kodlama işlemlerinde sıklıkla kullanan try/catch yapısıyla bir kullanım yapabiliyoruz. Bu işlemde catch içinde exception’un türüne ve gelen mesaja göre gelen exception’u kontrol edebiliyoruz.

Exception testleri yazılan kodun istediğimiz şekilde davranmadığında oluşabilecek durumları da kontrol altında tutmak için yazılır. Kayıt işleminde hata aldığında nasıl davranması gerektiğini belirlemek için hatayı yakalamak gerekir.

Bir diğer kontrol şekli ise test fonksiyonu oluştururken gelmesini beklediğimiz exception’u belirtmektir. Bu kodlamanın dezavantajı ise hata mesajı üzerinde bir kontrol sağlayamamamızdır.

Rule yapısıyla aslında try/catch mantığında olduğu gibi exception’a özel olarak kontroller yapabiliriz. Try/catch işleminden farkılı olarak rule ile yazdığımızda hata sonradan doğrulama ile kontrol ediliyor, bu doğrulamayı junit yapıyor.

AssertThat kontrolü ile istisnalar kontrol edilebilir. Verilen işlemin test edilmesi ve sonucunda oluşan hatanın içeriğine göre özel kontroller sağlanabilir.

Bu yazıda Java ile test konusuna temel anlamda bir giriş yaptık. Java ile Test 101 Bölüm 2 içerisinde; parametreli testler, grup testleri, Hamcrest, Mockito ve Project Lombok yardımcı kütüphanelerini kullanarak Java’da test yazmaya devam edeceğiz.

Herkese sağlıklı, mutlu günler.

--

--

Burak Acar
Burak Acar

Written by Burak Acar

Software Developer at Softtech

No responses yet