ShoppingTesting
MVVM Projesi ile Derinlemesine Android Test Driven Development
Install / Use
/learn @halilkrkn/ShoppingTestingREADME
MVVM Projesi ile Derinlemesine Android Test Driven Development(A-TDD)
- Bu repo'da Android Testing üzerinden Tüm Test işlemleri nasıl yapılır bu konuda öğrendiklerimi sizlere yorum satırları üzerinden anlatmaya çalıştım.
- Bir MVVM projesi üzerinden Unit Test, Integration Test ve UI Test'in nasıl yapıldığını yorum satırları ile Medium makalesi tadında anlatmaya çalıştım.
- Sadece yapmanız gereken, Branches'ları aşamalı bir şekilde takip ederek kodlar üzerindeki kendi yorum satırlarım aracılığı ile ileyelebilirsiniz/öğrenebilirsiniz.
- Neredeyse Testing olduğu her yerde açıklayıcı yorum satırlarımı göreceksiniz.
- Aslında hedefim kodlar içerisinde gezinerek ne, nerde, nasıl kullanılmış incelemeniz/öğrenmeniz.
- Eksiklerim elbette mevcut ama bunu en aza indirgemeye çalıştım umarım faydalı olur. Discussions kısmından geri bildirimlerinizi bekliyorum🤓
- Burada ilk önce genel bir Android'de Test Driven Development(TDD) yapısını anlatmaya çalıştım. Eğer olmazsa Intro'yu geç yapıp kodlara geçebilirsiniz.
Android'de TDD Nedir/Neden Kullanılır?
- Bir uygulamayı test etmek, uygulama geliştirme sürecinin ayrılmaz bir parçasıdır.
- Uygulamanıza yönelik testleri tutarlı bir şekilde çalıştırarak, kullanıcının önüne sürmeden önce uygulamanızın doğruluğunu, işlevsel davranışını ve kullanılabilirliğini doğrulayabilirsiniz.
- Testing'in bir diğer amacıda uygulama geliştirme sürecinde oluşabilecek hataları yakalayıp ileriki aşamalarda oluşabilecek hataları engellemektir. Bu şekilde iş yükünden ve zaman kayıplarından kurtulmuş oluruz.
->Android TDD'de Ana Prensip(Main Principle):
- Bir test uygulamasında fonksiyonun uygulanmasından önce test senaryoları yazılır.( sadece Unit Testler için)
- Daha içeriği olmayan bir fonksiyon tanımlanır.
- Fonksiyonu test etmesi gereken test senaryoları yazılır. Bu test senaryoları her zaman ilk başta başarız olması beklenir. Çünkü bu fonksiyonda herhangi bir içerik yoktur.
- Testlerin geçmesi için fonksiyon mantığı(içeriği) yazılır.
- Test senaryosu başına yalnızca tek bir iddiaya sahip olunması gerekir.
- Başarısız bir test vakasının nedenini hemen bilmek isteriz.
- Bir test senaryosunda birden fazla test iddiası olmamalı.
-> Android'de TDD'in Avantajları:
- Test, uygulama geliştirme sürecinin ayrılmaz bir parçasıdır.
- Uygulamaya yönelik testleri tutarlı bir şekilde çalıştırarak, tüm kullanıcının önüne çıkmadan önce uygulamanın doğruluğunu, işlevsel davranışlarını ve kullanabilirliğini test etmiş oluruz.
- Daha hızlı ve daha tekrarlanabilir testler gerçekleştirilmesinde yardımcı olur.
- Uygulama üzerindeki hatalar hakkında hızlı geri bildirim sağlaması.
- Uygulamayı geliştirme döngüsünde hataları erken tespit etme
- Uygulama içerisindeki kodların daha güvenli kodlar olmasını ve kodları yeniden düzenlenebilir halde kodu optimize etmemize olanacak sağlar.
- Uygulama üzerinde kararlı bir şekilde uygulamayı geliştirme hızını arttırır ve en aza indirgemeyi sağlar.
- kaynaklar: https://developer.android.com/training/testing
-> Android'de Bir TDD Stratejisi Tanımlama:
-
Normal şartlar altında uygulamanızdaki her kod satırını, uygulamanızın uyumlu olduğu her cihazda test etmeniz gerekir. Ama bu durum pratik olamayacak kadar yavaş ve maliyetlidir. (Böyle bir şeyi hiçbir şirket istemez tabi.)
-
<b>İyi bir Test Stratejisinde</b>, bir testin;
-
- <b> Doğruluğu </b>
-
- <b> Hızı </b>
-
- <b> Güvenirliliği </b>
arasında uygun bir denge bulunur.
-
-
Uygulamanızda oluşturmuş olduğunuz testleri JVM üzerinden düşük doğruluk testleri çalıştırılabilir ama daha hızlıdır.
-
Emülatör veya fiziksel cihazların kendisinde daha yüksek doğruluk testler çalıştırılabilir ama daha yavaştır.
-
Bu nedendele yüksek doğruluk testleri genellikle daha yavaş olduğundan daha fazla kaynak gerektirir. O yüzden her testi yüksek doğruluk test'i içerisinde yapılmamalıdır.
-> Flaky Testler (Kesintili Testler):
- Doğru tasarlanmış ve uygulanmış test çalışmalarında bile hatalar olabilir.
- Örneğin, gerçek bir cihazda test çalıştırırken, testin ortasında otomatik güncelleme başlayabilir ve başarısız olmasına neden olabilir.
- Yani test çalışması bazen başarılı bazen başarısız sonuç veriyorsa bu Flaky Test'tir.
-> Android'de Test Stratejisi Tasarlama:
- Scope (Kapsam)
- Yani Test Senaryosunun Kapsamı, test etmek istediğimiz fonksiyondaki gerçek kodun ne kadarının tek bir test senaryosu tarafından kapsandığını belirler.
- Speed (Hız)
- Bu sadece test senaryosunun ne kadar hızlı çalıştığı anlamına gelir.
- Test ne kadar hızlı çalışırsa test senaryoları o kadar sık çalışır ve daha çok hata bılur.
- Fidelity (Uygunluk)
- Test senaryosunun gerçek bir senaryoya ne kadar yakın doğrulukta olduğu anlamına gelir.
- Yani test edilen kodun bir kısmı bir ağ isteği yapması gerekiyorsa test kodu bu ağ isteğini gerçekten yapıyor mu yoksa sonucu taklit mi ediyor? Test gerçekten ağ ile iletişim kuruyorsa bu daha yüksek uygunluğa sahip olduğu anlama geliyor.
Android'de 'Test'edilebilir Mimari:
- Test edilebilir bir uygulama mimarisi ile kod, farklı bölümleri tek başına kolayca test etmenize izin veren bir yapı izler.
- Test edilebilir mimarilerin daha iyi <b>okunabilirlik, sürdürülebilirlik, ölçeklenebilirlik ve yeniden kullanıbilirlik</b> gibi avantajları vardır.
-> Test Edilemeyen bir Mimari:
- Daha büyük, daha yavaş, daha düzensiz testler üretir. Unit(Birim) Testi yapılamayan sınıfların daha büyük Entegrasyon(Integration) Test'leri veya UI Test'leri kapsamında olması gerekir.
- Farklı test senaryoları daha az test fırsatı oluşturur. Yani daha büyük testler daha yavaştır. Bu nedenle bir uygulamanın tüm olası durumlarını test etmek gerçekçi değildir.
-> Test'leri Ayrıştırma Yaklaşımı:
- Bir fonksiyonun, sınıfın veya modülün bir kısmı diğerlerinden ayrıştırılabilirse, test etmek daha kolay ve daha etkilidir.
- Bu Uygulama Ayrıştırması olarak bilinir ve Test Edilebilir Mimari için en önemli kavramdır.
- Yaygın Ayrıştırma Teknikleri:
- Bir uygulamada Presentation, Domain ve Data Layer(katmanlarının) ayırılması.
- Activity ve Fragments'lar gibi büyük bağımlılıkları olan entities'lere logic eklemekten kaçınılmalıdır. Bu sınıflar frameworklerin giriş noktaları olarak kullanılması ve UI(Kullanıcı Arabirimi) ve Business Logic'leri yani örn. ViewModel, Domain Layer gibi başka bir yere taşımak.
- Business Logic içeren sınıflarda doğrudan framework bağımlılıklarından(dependencies) kaçınılmalı. Örneğin, ViewModel içinde Android Context yapılarının kullanmaması.
- Bağımlılıkların değiştirilmesini kolaylaştırın. Bir Dependency Injection(DI) kullanılması.
Peki, İyi Bir Test için Ne Yapılmalı?
- İyi bir test senaryosunda hangi doğru özelliklere sahip olunması gerektiğini bilmek/düşünmek gerek.
- Ve iyi bir Test Senaryosu 3 karakteristik özelliğe sahip olmalıdır;
- Scope(Kapsam)
- Speed(Hız)
- Fidelity(Uygunluk)
- Kesintili(Flaky) bir test olmamalıdır. Yani test bazen başarılı veya bazen başarısız olmamalıdır.
- Asla bir testin sonucunu başka bir testin sonucuna bağla hale getirilmemelidir. Yani Her zaman testlerinizin bağımsız olduğundan ve başka bir testin senaryosuna bağlı olmadığından emin olunmalıdır.
- Herhangi bir test senaryosunun başka bir test senaryosunun sonucunu etkilemediğinden emin olunması gerekir.
Test İzolasyonu ve Bağımlılıkları
- Bir class'ı veya bir yapıyı test etmek istenildiğinde bu testler tekil olarak yapılmalıdır.
- Örneğin, Bir ViewModel'i test etmek için Android Frameworklerine yani Android Bileşenlerine bağlı olmaması gerektiği için Emülatör veya fiziksel cihazda başlatılması gerekmez.
- Ama bununla birlikte, test edilen yapının veya class'ın çalışması için başka yapılara/sınıflara bağımlı olabilir. Mesela, bir ViewModel çalışması için başka bir Data Yapısına/Class'ına bağlı olabilir.
- Bu yüzden Test edilen bir yapıya bağımlılık sağlanması gerektiğinde yaygın olarak Test Doubles(Test Çifti veya Test Nesnesi) oluşturulur.
-> Test Doubles(Test Çiftler/Nesnesi) Nedir?
- Test Çiftileri, uygulamanızdaki bileşen gibi görünen ve hareket eden nesnelerdir, ancak testinizde belirli bir davranış veya veri sağlamak için oluşturulur.
- En önemli avantajı, testlerini daha hızlı ve daha basit hale getirmeleridir.
-> Test Doubles(Test Çiftler/Nesnesi) Türleri:
- Birçok çeşit Test Doubles'lar vardır.
- Bunların en çok kullanılanı Fake, Mock, Stub dır.
- Bunlar;
- Fake, Gerçek bir projedeki çalışan bir class'ın test dizinlerinde kullanılabilmesi için fake bir class'ı oluşturulur.
- Çünkü ilgili class'ın gerçek projedeki class'tan bağımsız bir yapıda olması beklenir ve bir test çifti oluşturulur.
- Örneğin, Bir repository için FakeRepository oluşturulur ki test dizinlerinde senaryolarına uygun bir şekilde fonksiyonları yazılır.
- Bir başka örnekte, uygulama için veritabanı kullanımı için kullanılır. Diğer test çiftlerine kıyasla sistemin gerçek davranışına daha yakındır.
- Kod içerisinde Fake Test Doubles Kullanımı olayını inceleyebilirsiniz.
- Mock, Yazılan bir kodun veya bir class'ın bağımlı olduğu objelerin sahte bir referansını oluşturmamıza olanak sağlayan bir test double'dır.
- Yani bu şekilde ilgili nesneyi mock sayesinde taklit ederek sahte bir nesnesi oluştulan yapı test edilen sistemin yapılması beklenen işlemlerin yapılıp/yapılmadığını doğrulamak olarak tanımlanabilir.
- Bu yapıyı kullanabilmek için Mockito framework'ü projeye implement edilmelidir.
- Kod içerisinde [Mock Test Doubles Kullanımı](https://github.com/halilkrkn/ShoppingTesting/blob/f0007e1240cc6cdb9fc1e4a8c7c17bb132da1b35/app/src/androidTest/java/com/example
- Fake, Gerçek bir projedeki çalışan bir class'ın test dizinlerinde kullanılabilmesi için fake bir class'ı oluşturulur.
