CDC(Consumer-Driven Contract) Test
Microservis mimarisi, servisler arasında bircok iletisim icerir. Zaman içerisinde servislerin ürettiği veriler değişebilir, bu da aralarında input ve output bekletilerini içeren bir contract oluşturmalarına neden olur. Contract testler ile, microservisteki bileşenlerin bu contract’ı karsıladığı doğrulanır ve input/output değişimlerinde doğrulamadaki hatalarla kırılan yerlerin düzeltilmesi sağlanır.
Consumer-driven demek, contract’ın consumer tarafında başlamasından kaynaklanıyor, fakat bu contract her iki tarafta da çalışıyor.
Pact, consume-driven contract desteği sağlayan bir test framework’ü. Mock/Stub Http Server ile, consumer’ın istekleri ve beklediği responselar mock’lanır, böylece gerçekten provider tarafına istek atılmaz. Consumer tarafındaki bu isteklerden pact contract file adında bir kayıt oluşturulur ve pact broker’da saklanır. Bu, contractların saklanması ve paydaşlar arasında paylaşılması için repository görevi görür. Aynı istekler, bu sefer contract içindeki diğer tarafta koşturulur. Provider, bu contract’ı çeker ve consumer tarafında mock’lanan istekleri kendi üzerinde gerçek isteklerle çalıştırır ve dönen response ile contract’daki response karşılaştırılır.
Aslında yukarıdaki akışın, Spring üzerine kurulu mikroservislerde CDC testleri koşturmak için yazılmış Spring Cloud Contract kütüphanesinden farkı yok.
Pact, JUnit test framework’ü ve Maven/Gradle gibi build tool’ları ile sıkı sıkıya entegrasyona sahip. Consumer tarafında testi hazırlamak için Pact extension’ı ve bir annotation yeterli. Bu extension ile, Mock Http server ayarlanır ve pact contract file oluşturulur. @Pact annotation ile de, iletişimdeki input/output expectation’lar tanımlanır.
@Rule
public PactProviderRule mockProvider = new PactProviderRule("test_provider", "localhost", 8080, this);
@Pact(provider="test_provider", consumer="test_consumer")
public PactFragment createFragment(PactDslWithProvider builder) {
return builder
.uponReceiving("a request for something").path("/hello").method("POST").body("json")
.willRespondWith().status(200).body("json")
.toFragment();
// PactDslJsonBody builder sınıfı ile, static tanımlı body'ler yerine dynamic şekilde oluşurulabilir.
}
Provider tarafındaki testte contract’ları doğrulamak için JUnit testi yada maven pluginleri kullanılabilir. Maven plugin ile, pact broker url’i ile yayınlanan contract’lar çekilir, mvn pact:verify ile doğrulanır.
Pact State’ler ile, consumer ve provider arasında bilgi paylaşımı sağlanır, böylece provider’da testler başlamadan önce provider’a özgü ayarlamalar yapılır. Mesela ne gibi? Veritabanında benim gönderdiğim verileri ekleyerek test işlemini bu veriler üzerinden gerçekleştir gibi.
JUnit5
JUnit4’den 5’e geçerken testleri yeniden düzenleyebileceğinizi düşünebilirsiniz, fakat JUnit5, JUnit4 testlerini Vintage library kullanarak çalıştırabilir. böylece eskiler üzerinde yoğunlaşmadan sadece yeni testler JUnit5 kullanılarak yazılabilir.
Farklar
- importlar org.junit yerine org.junit.jupiter.api package altında
- @Test annotasyonu parametre almak yerine, özel assertions fonksiyonlara bırakıldı. mesela test sonucu beklenen hatayı belirtmek için expected property yerine assertThrows yada assertDoesNotThrow ile bu kontrol yapılacak.
- annotation isimlendirmeleri değişti. mesela before ve beforeclass daha anlaşılabilir hale getirilmiş. before -> beforeEach, beforeClass -> beforeAll, ignore -> disabled, category -> tag.
- assertions altındaki hata mesajları son arguman olarak geçiliyor. assertion fail olması durumunda error mesajı hazırlamak için lambda kabul edebilecekler.
- assumptionlarda, condition karşılaşırsa çalışıtırılacak code lambda ile sağlanabilir hale gelebilir.
- JUnit’de framework’ü yapılandırmak icin RunWith kullanarak extension’lar belirtiliyor, mesela @RunWith(SpringJUnit4ClassRunner.class). JUnit4’de her class için yalnız bir tane extension belirtilebiliyorken, daha fazlası için Rule ve ClassRule annotation’lı değişkenler tanımlamak gerekiyordu. JUnit5 ile extensionlar geliştirildi. Artık RunWith/Rule ve Classsrule yerine, ExtendWith ve RegisterExtension ile birçok extension kullanılabilir.
Yeni Eklenenler
- @DisplayName. sınıf ve test-case’lerden önce, amaçlarını belirtmek için kullanılır.
- @Test propertylerini karşılayacak yeni assertions methodları.
- @Nested ile nested test classları. böylece tek bir sınıfın farklı amaçlarla yazılan test-case’leri farklı test sınıflarında yazılmak yerine aynı nested class’ta yazılır, böylece benzer test-case’ler aynı nested class’ta gruplanmış olur.
- parameterized tests. JUnit4’de bu dışarıdan sağlanan library ile vardı, fakat artık built-in destek var.
@ParameterizedTest @ValueSource(strings = {"foo", "bar"}) // source tipleri; value, empty, null, enum, arguments, method, csv public void test(String args) {}
- @RepeatedTests ile testi istenilen sayıda tekrar çalıştırma
- conditional test execution. belirtilen koşula göre @Disabled olup olmamasını belirliyor. @EnabledOnX, @EnableIfX
- test templates ve dynamic tests. detayları araştırınca ekleyeceğim.
Micronaut ve SSE
Bu iki konu üzerine bir örnek geliştirme yazısı var, bu ikisini ilk defa duydum.
Micronaut is a modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. Micronaut has built-in support for Kafka, RabbitMQ, and two HTML5 messaging paradigms: server-sent events (SSE) and WebSocket.
Micronaut, Spring Boot ile karşılaştırıldığında daha hızlı ayağa kalkma (runtime yerine compile time dependency injection’dan dolayı), daha düşük memory tüketimi ve daha fazla isteğe cevap verebilme kabiliyetine sahip.
SSE(Server-Sent Events), WebSocket’e benzer bir yapı. İkisi de push technology, fakat WebSocket’te bi-directional veri akışı varken, SSE’de sadece server’dan client tarafına veri akışı var.