Giriş

Önceki makalemde bu konuya giriş yaptığım için konuya devam ediyorum.
Android Geliştirme İçin Best Practices — Part 1 yazıma buradan göz atabilirsiniz.

- Proje Uygun Testlerle Kontrol Edilmeli

  • A/B Testing: Güvenliği artırmak, yeni özellikler sunmak veya işlevselliği genişletmek için yapılan güncellemeler, her mobil uygulama geliştirme sürecinin yaşam döngüsünün bir parçasıdır. Ancak kullanıcıları büyük uygulama değişiklikleriyle bunaltmak asla iyi bir fikir değildir. Örneğin, büyük UI değişikliklerini yalnızca daha küçük bir temsili kitleyle A/B testinden geçtikten sonra daha geniş bir kitleye sunmak her zaman daha iyidir.
    Aşamalı Sürüm, sürüm sürecinin risklerini ortadan kaldırdığı gibi çevik metedolojisininde bir parçasıdır. Tüm ekibin her sürümün altında yatan üst düzey iş hedefini anlamasını sağlamak için çıktılara göre sonuçlara öncelik verir. Geliştirme ekiplerinin belirli işlevleri belirli kullanıcı segmentlerine ve belirli zamanlarda yayınlamasına olanak tanır, böylece riski artırmadan pazar sürüm süresini hızlandırır.
  • Birim Test (Unit Test):

a) Unit Test, bir yazılımın en küçük test edilebilir bölümlerinin, tek tek ve bağımsız olarak doğru çalışması için incelendiği bir yazılım geliştirme sürecidir. Buradaki amacımız yazılımın her biriminin tasarlandığı şekilde gerçekleştiğini doğrulamaktır. Fonksiyonların çalışıp çalışmadığını birim test ile kontol edebiliriz.

b) Unit Test, test edilen özelliğin açık bir tanımını yapmalıdır.

c) Unit Test yazılım testinin ilk seviyesidir ve entegrasyon testinden önce gelir.

d) Unit Testleri geliştiriciler kendileri yazar ve yürütürler.

e) Unit Test’ ler tüm hataları ortaya çıkarmaz, çünkü her parça izole şekilde test edilmekte ve entegrasyon yapıldığında herşeyin düzenli çalışacağı anlamına gelmez.

f) Unit Test yazmak kodda yeniden düzenleme(Refactor) işlemini yapmayı kolaylaştırır. Kodda değişiklik yaptığımızda, Unit Testi çalıştırıp oluşturduğumuz algoritmaya uygun bir şekilde çalışıp çalışmadığını kolaylıkla test edebiliriz.

g) Yazdığınız testin tek bir birimi test ettiğini unutmayın, aksi taktirde yazdığımız test birim test değildir.

h) Test sayısı hatasızlığı sağlamaz. Risklerinizi ne kadar azaltırsanız, kalitenizi o kadar yukarıya çekebilirsiniz.

Bazı Unit Test Framework’ leri:

1.Robot Framework, 2. JUnit, 3. Mockito ,4. Spock, 5. NUnit, 6. TestNG, 7. Jasmin, 8. Mocha, 9. Powermock…

Unit Test nasıl yazılır?

Geliştireceğimiz yazılımları yazmadan önce Unit Test’ lerini yazmamız gerekiyor ve Unit Test yazmak için de bazı kurallara uymamız gerekiyor.

1. En küçük parçacığı test edilmeli

2. Sadece bir senaryo test edilir.

3. Kullanılan adımlar belirlenir.

4. Test method ismi test edilen senaryonun yansıması olmalıdır.

5. Test edilen kısım diğer kısımlardan bağımsız olmalıdır.

6. Testlerimiz tam otomatik şekilde çalışmalıdır.

7. Hızlı çalışabilmeli ve çabuk sonuçlar vermelidir.

8. Okunaklı, anlaşılabilir ve sürdürebilir olmalıdır.

9. Test başarısız olduğunda durmalı ve iyi bir hata raporu döndürmelidir. Bu hata raporunda neyi test ettin ? ne yapmalı ? beklenen çıktı neydi ve gerçekte ne yaptığıdır ?

  • Unit Test ikiye ayrılır. Bunlardan ilki local unit test,ikincisi ise intstumentation test tir. Local unit testler için bir android cihaza ya da emülatöre ihtiyaç olmazken instrumentation testler bir cihaz yada emülatör üzerinde gerçekleştirilir.
  • Local Unit testler app>src>test içerisinde bulunur. Projeniz de Android görünümünü seçtiğiniz zaman Local Unit Testlerin bulunduğu paket java klasörünün altında, (test) şeklinde parantez içinde bir ekle gözükür. (anroidTest) yazan ise instrumentation testlerin bulunduğu pakettir.
  • Testi başlatmak için test etmek istediğiniz methodun yanındaki yeşil ok simgesine (run) tıklayabilirsiniz. Eğer bütün methodları aynı anda test etmek istiyorsanız sınıf isminin yanındaki yeşil ok simgesine ya da test paketi içerisinde test sınıfına sağ tıklayarak run seçeneğini seçebilirsiniz. Aşağıda bu sınıfın tüm methodlarının test edildiği bir durumu çıktısı verilmiştir.

Not: JUnit ve Espresso bağımlılıkları projeye dahil olarak yer almaktadır. Ayrıca eklemeye gerek yoktur.

Test Senaryosu

Test senaryosu, test gerekliliklerinin, fonksiyonelitiye göre üst seviye bir sınıflandırılmasıdır. Bu dokümantasyonda test edilecek fonksiyonların senaryolarını dokümante etmeniz beklenir ve çok detaylı yazılmazlar. Test edilecek bir modülünüz var, bu modülün fonksiyonlarını test etmek için belirlediğiniz kriterlerin dokümantasyonudur.

Test durumu (Test Case) nedir?

Test durumu, başarılı ve başarısız yöntemlerle koşulabilecek test senaryosu prosedürleridir. Bir test senaryosu sonunda başarılı bir işlem gerçekleşmesini bekliyorsanız başarılı bir test durumunuz olur. Tam tersi olarak, test senaryosu sonunda başarısız bir işlem bekliyorsanız negatif bir test durumunuz olur. Tabii bu noktada, pozitif test durumları başarılı, negatif test durumları başarısızdır yargısı oluşmamalıdır. Negatif test durumlarınızın sonunda negatifi yakalamalısınız, pozitif test durumlarınızda ise pozitifi yakalamalasınız. Eğer test sonuçlarınızda elinizdeki sonuçlar eşleşiyorsa, testinizi başarılı ilan edebilirsiniz.

Analiz dokümanındaki her gereksinime karşılık sadece bir senaryo mu olmalı mıdır?

Aslında bu soruya hem evet, hem hayır denebilir. Buna sebep ise; testin içerik bağımlı olmasıdır. Eğer test ettiğiniz fonksiyon sadece bir butona tıklandığında içerik detayının getirilip getirilmediği ise burada sadece bir test senaryosu yeterli olabilir. Ancak, bir uçaktaki yere yaklaşma sistemini test ediyorsanız sanırım bir gereksinme karşılık 100 testiniz bile olabilir.

Bir ekrandaki testleri bir senaryo altında toplayıp, yüzlerce adıma sahip bir test senaryosu oluşturmayın. Senaryolarınızı mümkün olduğunca bölüp, test adımlarını her test senaryosu için ayrı yazmanız daha doğru olacaktır. Böylece, geliştirme ekibiniz de sistemde nelerin test edildiğini başlık halinde görme şansına sahip olacaktır.

Test Adımı nedir?

Test adımı, bir ekrandaki test edilecek şeylerin tek tek yazıldığı yer değildir. Test adımı, bir test senaryosunun gerçekleştirilebilmesi için gerekli adımlarının açıklandığı dokümantasyondur.

Örnek:

Fonksiyon: Login Fonksiyonu

Ön Koşul: Kullanıcının login olabilmesi için, daha önceden başarılı bir üye kaydı olmalıdır.

Gereksinimler:

o Kullanıcının eposta adresini girmesi için bir text box olmalıdır.

o Kullanıcının şifresini girmesi için bir text box olmalıdır.

o Kullanıcının bu bilgileri server a göndermesi için bir buton olmalıdır.

Test Senaryoları:

  • E-posta alanına fazla karakter girilmesi -> Test Durumu: Negatif
  • Şifre alanına fazla karakter girilmesi -> Test Durumu: Negatif
  • E-posta alanına e-posta formatında bir değer girilmemesi -> Test Durumu: Negatif
  • E-posta ve şifre alanlarına doğru bir değer girilmesi -> Test Durumu: Pozitif
  • E-posta doğru şifresi yanlış bir değer girilmesi -> Test Durumu: Negatif
  • Alanların hiçbir değer girilmeden formun gönderilmesi -> Test Durumu: Negatif
  • Gönderme butonuna iki kere tıklanması-> Test Durumu: Negatif
  • Daha önce üye olmayan bir e-posta ile login denemesi-> Test Durumu: Negatif

İzinler (Permissions)

Android, marshmallow sürümünden bu yana uygulamanın kullanım noktasında bazı izinler talep etmesini sağlayarak yeterli gizliliği sağlamkatadır. Buna aynı zamanda çalışma zamanı izinleri(runtime permissions) denilir. Uygulamanızda, yalnızca düzgün çalışması için gereken minimum sayıda izin istenmelidir.

Veri depolama(Data Storage)

Uygulamalarımızın her zaman veri depolaması gerekir. Verilerin, diğer uygulamaların erişemeyeceği şekilde güvenli bir şekilde saklanması gerekiyor. Android sistemi, harici depolamada bulunan veriler üzerinde güvenlik kısıtlamaları uygulamadığından ve depolama ortamının kendisinin cihaza bağlı kalması garanti edilmediğinden, hassas verilerin harici depolama yerine dahili depolamada depolanması önerir. Ek olarak, küçük miktarda veri depolamak için SharedPreferences kullanmanız gerekiyorsa, diğer uygulamaların erişememesi için özel modda kullanılması hatta şifreleme yapılması tavsiye edilmektedir.

Performans

Uygulama geliştiricileri olarak Android’de kaynak yönetimi konusunda bilgi sahibi olmalıyız. Uygulamaya ayrılan belleği iyi yönetebilmeli, cihazın gereksiz yere kullanılmasını önlemek için optimum kullanımı sağlamalıyız. Uygulamalarımızdaki performansı artırmak için aşağıdaki ipuçlarından yararlanabiliriz;

  • İlk olarak, gereksiz nesneler oluşturmaktan kaçınmalısınız. Android belgelerine göre, bir nesnenin alanlarına erişmeniz gerekmiyorsa (örneğin bir sınıfın üye değişkenlerine erişim), yönteminizi statik yapmalısınız çünkü statik çağrılar yaklaşık %15–20 daha hızlıdır. Kotlin dilinde, bu snippet’te görüldüğü gibi nesnelerin gereksiz başlatılmasını önlemek için tembel başlatma konsepti sağlar:
private val mAdapter by lazy {
ViewPagerAdapter()
}

Bu, nesnenin yalnızca erişilmek üzereyken başlatıldığından ve nesnenin yalnızca bir örneğinin var olduğundan emin olur.

  • Bellek sızıntılarının, uygulamanın donması ve sonunda bir OutOfMemory (OOM) hatası nedeniyle uygulamanın çökmesi gibi ciddi etkiler oluşturabilir. OOM hataları, bir işlemin maksimum yığın bellek sınırını aşan çok sayıda bellek talebi olduğunda ortaya çıkar. Square, uygulamamızdaki bellek sızıntılarını izlememize yardımcı olan LeakCanary adlı açık kaynaklı bir kitaplığa sahiptir. Bu ve benzeri kütüphaneler ile bellek sızıntılarının önüne geçebilirsiniz.

“A small leak will sink a great ship.” — Benjamin Franklin

Validation & Verification

Kullanıcılardan alınan girdileri(input) çeşitli en doğru şekilde alınmasını sağlanmalıdır. Bu tarz durumları gözetilmesi bazı hataların alınmasını önleyebileceği gibi uygulamanın ve kodun da kalitesini arttıracaktır. Örneğin kullanıcıların e-postası geçerli mi veya Kullanıcının iletişim numarası gerekli uzunlukta mı vb. gibi doğrulamalar yapılması gerekmektedir.

İş Parçacıklarını İyileştirme(Optimization Thread)

Uzun süredir devam eden tüm görevler için bir arka plan iş parçacığı oluşturulabilir. Bunu yapmak, uzun vadede daha iyi bir performans ve daha iyi bir kullanıcı deneyimi için önemlidir. Bunun için Android Studio, genellikle ekranınızın altında bulunan ve yalnızca uygulama çalışırken görünen, kodunuz geliştirme aşamasındayken uygulamanızın performansını kontrol edebileceğiniz bir profil oluşturucu sağlar. Profil oluşturucu, yürütme sırasında cihazın uygulamanızın kaynak kullanımını ölçmenize yardımcı olur.

Birden Fazla Cihaz Desteği:

Android cihazlar, dünya üzerinde farklı sürüm, boyutlar, marka ve modellerde bulunmaktadır. Uygulamanızın daha geniş kitlelere ulaşmasını sağlamak için mümkün oldukça desteklenebilecek tüm versiyonları destekleyebilirsiniz. Bu tarz uygulamalar, kullanıcı sayınızı oldukça arttıracaktır.

Çoklu Dil Desteği:

Dikkat edilmesi gereken bir diğer konu ise çoklu dil desteğidir. Daha fazla dil eklemek, daha fazla kişiye ulaşmak demektir. Bu potansiyeli yüksek özelliği muhakkak uygulamalarda kullanmalıyız. Bunu başarmak için tüm dizelerinizi strings.xml dosyasında saklamanız gerekir. Bu çeviri içinde kolaylık sağlar. Diğer dilleri desteklemeyi planlamıyor olsanız bile, tüm dizeleri strings.xml dosyasında saklamak uygulanması gereken bir standarttır.

Build Types Kullanımı:

Bu özellik kodumuzun nasıl derleneceğine belirleyebilmemizi sağlar. Örneğin, .apk’mizi debug anahtarı ile imzalamak istiyorsak, debug konfigürasyonumuzu debug build tipine koyarız. Derlendiğinde ve yayınlanmaya hazır olduğunda gizlenmiş koda sahip olmak istiyorsak, bu yapılandırmayı yayın(release) yapı türümüze koyarız. HTTP isteğimizi hata debug modunda günlüğe kaydetmek istiyor ancak yayın modunda(release mode) devre dışı bırakmak istiyorsak, bu yapılandırmayı derleme türlerine(build types) koyarız veya derleme türlerini kitaplık bağımlılıklarında çağırırız.

Uygulamanızı hata ayıklama modunda çalıştırdığınızda, uygulama paketi adınız paketadı.debug olur ve uygulamanızı sürüm modunda çalıştırırsanız, uygulama paketi adınız sadece paket adı olur.

Product Flavor Kullanımı:

Product flavor, uygulamaları geliştirirken, derlerken ve imzalı versiyon hazırlarken, Gradle’ın kullandığı bazı özellikleri düzenleyebilmemizi ve bizim kullanacağımız bazı konfigürasyonları daha kolay yönetmemizi sağlar. Bunu bir örnek ile açıklayalım. Bir müşteri için uygulama geliştirdiğinizi düşünelim. Müşteri uygulamasında her şey yolunda giderken ürün sahibi, bu uygulamayı yönetici kullanıcılar için geliştirmeniz gerektiğini söyler. Yönetici kullanıcı uygulaması, müşteri uygulamasının sahip olduğu tüm işlevlere sahip olmalıdır. Ancak yönetici kullanıcı da istatistik sayfasına erişebilir ve yönetici kullanıcı uygulamayı farklı renklerde ve kaynaklarda görmelidir. Ayrıca yönetici uygulamanızın analitiği müşteri uygulamasıyla karıştırılmamalıdır. Bu tarz senaryolarda producy flavor yapısı size aynı uygulamada, farklı davranışlar geliştirmenizi sağlar.

Build Variants Kullanımı:

Build types ve product flavor yapılarını birleştirir. build.gradle’ınızı güncelledikten sonra projenizi senkronize edebilir ve ardından tüm yapı çeşitlerinizi görüntüleyebilirsiniz.

Sonuç

Bu ve benzeri bilgiler, temiz ve sürdürülebilir kod yazmamıza yardımcı olur. Bu makalede, Android geliştirmede çoğu geliştiricinin küçük veya büyük hatalar yapma eğiliminde olduğu, tasarım modellerinin önemi hakkında bilgi edinmekten, performansı iyileştirmeye, uygulamaların güvenceye alma gibi alanları hakkında kısa bilgiler vermeye çalıştım. Bu tarz noktalara uygulamalarımızda dikkat edersek hem kullanıcı deneyiminde hem de kod bakımında geliştirmek için kaliteyi oldukça arttırabiliriz.

--

--