AWS: DynamoDB için Testlerin Yazılması

DynamoDB’nin Java API’sinin nasıl kullanılacağına önceki yazıda deÄŸindik fakat uygulamamızı çalıştırarak test ettik. Her ne kadar bu tip blog yazılarında kabul edilebilir olsa da ciddi bir uygulamayı bu ÅŸekilde test edemezsin. Bu yazıda, DynamoDB kullanan ciddi bir uygulamanın, DAL (Data Access Layer) katmanını nasıl test edebileceÄŸimize bakacağız.

Ne yazıkki, genel olarak şu şekilde yazın diyebileceğim bir konu değil. Her test stratejisinin farklı avantajları farklı dezavantajları var. Bu sebepten Integration Test ya da Unit Test diye ayırmadan farklı test stratejilerini inceleyip avantajlarına dezavantajlarına bakalım.

Doğruca AWS Üzerinden Test Edilmesi

Akla ilk gelen çözümlerden biri. Testlerinizin çalışacağı bir DynamoDB tablosu yaratıyorsunuz ve tüm testleriniz bu tablo üzerinden çalışıyor. Integration Test gibi düşünebilirsiniz. Eğer kapasitenizin limitlerini ölçmek istiyorsanız kullanabileceğiniz tek strateji bu.

Avantajları

  • Uygulaması en kolay strateji. Sadece test veritabanınız için AmazonDynamoDBClient yaratmanız ve ilgili sınıflara saÄŸlamanız yeterli oluyor.
  • Load Test yapmak istiyorsanız, limitlerinizi ölçebileceÄŸiniz tek strateji.

Dezavantajları

  • Her ne kadar, Free Tier’da, 20 RCU ve 20 WCU hakkınız olsa da bu limiti testlerle aÅŸmak oldukça kolay. Bu sebepten bu yöntem en basit olduÄŸu kadarıyla da en maliyetli yöntem. Hatta tek maliyetli yöntem.
  • Tüm takım aynı testlerde aynı tabloyu kullanıyorsa testlerinizi yazarken bunu göz önüne almanız lazım. ÖrneÄŸin testlerinde sabit bir PartitionKey kullanıyorsanız, iki yazılımcı aynı projeyi derlediÄŸinde, False Negative almaları bir hayli olası. Bunu her test için farklı tablo yaratıp/silerek aÅŸabilirsiniz. Ama bu seferde maliyetinizi arttırma durumunuz var.
  • Testleriniz için gerekli olan veriyi sizin kaydetmeniz gerekiyor. Aynı ÅŸekilde yazdığınız verileri temizleme iÅŸide size ait.

İstemcinin Mock’lanması

Diğer akla gelen çözüm, istemcilerin mocklanması. Kullandığınız DynamoDB istemcisi neyse onu Mockito ve benzeri kütüphaneler yardımıyla mockluyorsunuz.

Avantajları

  • Her türlü durumu yaratmanız mümkün. Mesela kapasite aşımı yaptığınızda ProvisionedThroughputExceededException türünde bir hata alırsınız. Bu hatayı gerçek sunucu üzerinde gerçek veritabanını kullanarak oluÅŸturmak çok zahmetli olabiliyor. Fakat istemcinizi mockladığınızda istediÄŸiniz her hatayı rahatlıkla oluÅŸturabilirsiniz.
  • DiÄŸer test stratejilerine göre çok daha hızlı. Ne Network iÅŸlemi yapıyorsunuz ne baÅŸka birÅŸey.
  • Gerçek bir kayıt iÅŸlemi yapmadığınız için testlerden sonra bir temizleme iÅŸlemi yapmanıza gerek yok.
  • Testlerinizi yazarken diÄŸer etkenlerden tamamen izole durumdasınız. Önceki stratejide olduÄŸu gibi diÄŸer yazılımcıları düşünmenize gerek yok. False Negative alma ihtimaliniz neredeyse yok.

Dezavantajları

  • Aslında yine veriyi siz yaratıyorsunuz. Fakat bu sefer sadece yaratmakla kalmıyor, üzerine bu verileri kullandığımız istemcinin döneceÄŸi formata çevirmeniz gerekiyor. EÄŸer Java’nın High Level API’sini kullanıyorsanız, sadece nesne döneceksiniz, çok sorun yok. Ama Java’nın Low Level API’sini kullanıyorsanız, tüm Request ve Response türlerini bilip buna göre mock’lama yapmanız gerekiyor.
  • EÄŸer özel Query ya da Scan sorgularınız varsa, ki ciddi bir uygulamada büyük ihtimalle olacaktır, bunların geçerli olup olmadığını bilemiyorsunuz. Bunların neler dönebileceÄŸini belirtiyorsunuz. Ama gerçekten çalışıyor mu bilemiyorsunuz. DiÄŸer test stratejilerinden birini kullanıp özel sorgularınızı test etmeniz gerekiyor.

Lokal DynamoDB Sunucusu Kullanmak

Önerebileceğim son test yöntemi Lokal DynamoDB sunucusu kullanmak. Eğer daha önce duymadıysanız, AWS geliştiricilerini düşünerek, Lokal DynamoDB sunucusu sağlıyor. Bu sunucuyu kullanarak tüm veritabanı operasyonlarınızı yapabilirsiniz. Her ne kadar ZIP formatında indirip çalıştırmak çok kolay olsa da test sırasında ayağa kaldırmak zahmetli olabiliyor. Bir sonraki bölümde nasıl gerçekleyeceğinize bakacağız ama önce diğer stratejilerdeki gibi avantajlara ve dezavantajlara bakalım.

Avantajları

  • DoÄŸruca AWS üzerinden test edermiÅŸ gibi testlerinizi çalıştırabiliyorsunuz. Tek yapmanız gereken istemcinizin baÄŸlanacağı adresi AWS’ten Lokal’inize çevirmek.
  • Tamemen ücretsiz
  • Gerçek DynamoDB API’si üzerinden konuÅŸtuÄŸunuz için, lokal sunucuda çalışan tüm sorgularınız, AWS üzerinde ki sunucuda da çalışacaktır. Ek bir iÅŸlem yapmanıza gerek yok.
  • Lokal DynamoDB’ye testleriniz için kayıt oluÅŸturmanız gerekiyor ama sunucunuz bellekte çalıştığından temizleme yapmanıza gerek yok.
  • Testlerinizi yazarken diÄŸer etkenlerden tamamen izole durumdasınız.

Dezavantajları

  • Hemen hemen her türlü hata durumunu oluÅŸturabilseniz de limit aşımı durumunu Lokal DynamoDB sunucusu kullanarak oluÅŸturamıyor, test edemiyorsunuz.
  • Alt tarafta SQLite kullanıyor. Kullandığı SQLite kütüphanesinden dolayı Native kütüphane bağımlılığı var. Bu sebepten kütüphane olarak kullanımı biraz uÄŸraÅŸtırıyor.
  • Her test için veri setinizi yaratmanız gerekiyor.

Örnek Kullanım

Kütüphane olarak kullanımı biraz uÄŸraÅŸtırdığından bir örnek kullanım da paylaÅŸmak istiyorum. Mock’lamayı ya da doÄŸrudan AWS kaynaklarını kullanırken, yapılan iÅŸlemlerin bir karmaşıklığı olmadığından onlarda örnek kullanım yazmaya ihtiyaç duymadım. EÄŸer bir sorun yaÅŸarsanız, yorum olarak paylaşırsanız elimden geldiÄŸince yardımcı olmaya çalışırım.

Örnek kullanımda, bir önceki yazıda yazdığımız UserRepositoryImpl sınıfının testlerini, Lokal DynamoDB sunucusu kullanarak yazacağım. Bunu için öncelikle maven’a aÅŸağıdaki bağımlılıkları ve eklentileri ekliyoruz.

pom.xml

Burada dikkatinizi çekmek istediğim nokta her bir işletim sistemi için Native kütüphaneleri tek tek belirtiyoruz. Daha sonra eklenti kısmında bu kütüphaneleri kolayca erişebileceğimiz bir yere, projenin ana dizinindeki native-libs klasörüne kopyalıyoruz. Testlerimizi yazmaya başladığımızda bu yolu kullanarak, Native kütüphaneleri sistemimize tanıtacağız.

Her DAO için sunucuyu ayağa kaldırmakla, veritabanını yaratmakla uğraşmayalım diye bir JUnit kuralı yazalım. Bu kural bizim için uygun bir port seçip sunucuyu ayağa kaldırsın ve gerekli tabloları model sınıfından yaratsın. Bunun için projemizin src/test/java klasöründe com.bahadirakin.dynamodb.rules paketinin içerisine LocalDynamoDBCreationRule isminde sınıfımızı yaratıyoruz.

LocalDynamoDBCreationRule.java

Az önce belirttiğim gibi Constructor içerisinde native-libs klasörünü, sistem özellikleri üzerinden, SQLite kütüphanesine iletiyoruz. Ama dikkatinizi çekmek istediğim bir nokta daha var. Farkettiyseniz tablo yaratma isteklerini gönderirken kapasite kullanımını yine de belirtiyoruz. Bu az önce söylediklerimle çelişiyormuş gibi gelebilir. Burada ne değer belirtirseniz belirtin, hiç bir şekilde limit aşımı hatası almayacaksınız. Fakat bu değeri vermediğinizde, normal AWS servislerinde alacağınız hatanın aynısını alırsınız.

Son olarak ise bu kuralı testlerimizde nasıl kullanacağımıza bakalım. Bunun için projemizin src/test/java klasöründe com.bahadirakin.dynamodb.dao paketinin içerisine UserRepositoryImplTest isminde sınıfımızı yaratıyoruz.

UserRepositoryImplTest.java

Peki sen hangisini tercih ediyorsun?octobiwan

… Diye soracak olursanız, ben hepsinden gerektiÄŸi kadar kullanıyorum. Ama iÅŸin içine ücret kalemi girdiÄŸinden en son doÄŸrudan AWS servislerini kullanmayı seçiyorum. İlk tercihimse Lokal sunucu kullanmak oluyor. Tüm uç noktaları ise Mocklama yöntemiyle test ediyorum. Her ne kadar kendi Lokalimizde de çalıştırsak bazı uç durumları oluÅŸturmak gerçekten zor olabiliyor.

Git: https://github.com/bhdrkn/Java-Examples/tree/master/aws-dynamodb-tutorial