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