Service Virtualization

Semih Saydam
7 min readNov 1, 2020

--

https://www.northwaysolutions.com/

Merhaba,

Servis sanallaştırma uzun zamandır merak ettiğim bir konuydu. Bunun üzerinde yaptığım araştırmaları bu yazıda toplayıp sizlerle de paylaşmak isterim. Burada en büyük kaynak olarak Melih Sakarya’nın Webinar’ını kullanıyor olacağız.

Yukarıdaki görselde gördüğünüz üzere bir uygulamanın çoğu yere bağımlılığı vardır. Bu durum bizi engeller. Örneğin burada Mobile Application’u test ettiğimizi varsayalım. Mobile Application’un bağımlı olduğu database’e bağlantınız olmadığı durumda veya ödeme sistemindeki bakımdan dolayı sizin testiniz başarısız olabilir. Burada vakit kaybedersiniz. Testinizin başarılı olması için dışarı bağımlısınızdır. İşte bu bağımlılıkları kaldırabilmek için servis sanallaştırma yapılır.

Servis sanallaştırma, servis bağımlılıklarını ortadan kaldırır. Beklemeleri azaltır. Proje teslim süresini kısaltır.

Servisi sanallaştırırken birkaç yöntem önümüze çıkar. İlk olarak bu servisi yazabilirsiniz(var olmayan bir servis öncesinde sanal servis olarak tasarlanabilir). İkinci olarak service’in snapshot’ını alabilirsiniz. Yani servisi kayıt edip onu sanal servis haline getirebilirsiniz. Son olarak da burada ihtiyacınız olan servisi sahteleyebilirsiniz.

Bu sanallaştırma çok farklı durumda işinize yarayabilir. Örneğin test ortamında bir yük testi yapacaksınız. Siz bu testi yaparken bağlı olduğunuz service’lere de yük gidiyor olacaktır. Bağlı olduğunuz service’lerden biri bu yüke dayanamayacak durumda ise bu yükü o service’e göndermemelisiniz. Bu durumda yüke dayanamayacak service’i sanallaştırarak yükün gidişini engelleyebilirsiniz.

Servis sanallaştırmayı yapabileceğiniz birden çok araç vardır. Bu yazıda WireMock kullanıyor olacağız. WireMock açık kaynak service virtualization aracıdır.

Servislerinizin hepsi sanal olmak zorunda değildir yani bir kısmı sanal bir kısmı gerçek olabilir :

Testinium Webinar Series — Service Virtualization

Bu yazı kapsamında Melih Sakarya’nın yazdığı spring tabanlı bir proje üzerinden ilerliyor olacağız. Projenin Github linkine buradan ulaşabilir ve clone’layabilirsiniz.

Projeyi clone’ladıktan sonra test → java → com.testinium.sample → SampleWebProjectApplicationTest1'e konumlanalım.

Gördüğünüz üzere buradaki testleri çalıştırmak istediğimizde çalışmayacaktır. Bunun sebebi arkada çalışan service’in ayakta olmamasıdır. Yani istediğimiz case’e düşmüş olduk böylelikle. Service bir şekilde ayakta olmayacak ve bizim testlerimiz hata verecek. Bunun üstesinden gelebilmek için service virtualization’dan yararlanacağız. Şimdi test → java → com.testinium.sample → SampleWebProjectApplicationTest2'e konumlanalım :

Burada ilk olarak @ExtendWith(WireMockExtension.class) ile class başında bir tanımlama yaptık. Ardından @InjectServer ve @ConfigureWireMock ile Wiremock için ayarlamalarımızı yaptık. WireMock default olarak 8080 port’unda çalışıyor fakat bunu yukarıdaki gibi dilediğiniz port’a ayarlayabilirsiniz.

@Test ile ilk testimizin içine konumlanalım. Burada ‘stub’ oluşturduğumuza dikkat edelim. Unit Test yazı serisinde anlattığım üzere stub’lar gerçek nesnenin bir benzerinin oluşturulmasıydı. Mock ise sahte nesnenin oluşturulmasıydı.

Öncelikle stubFor(post(urlPathMatching(“/rest/user/add”))) kod satırıyla Post request add methodu için basePath’i veriyor olacağız. Service ayakta olsaydı buraya istek atıyor olacaktı :

Service’imiz ayakta olmadığı için önceki çalıştırdığımız testimizde bu hatayı vermişti. Gördüğünüz üzere http://localhost:9080/rest/user/add ‘e istek atmaya çalışıyor. urlPathMatching() ile biz de bunun basePath’ini verdik. Bu arada service’i gerçekten ayağa kaldırmak isterseniz buradaki linkten projeye ulaşabilirsiniz.

Ardından .willReturn() ile bize dönecek response’u şekillendiriyoruz. Dönmesini istediğiniz response’un status’unu, header bilgilerini, responseBody’sini burada düzenleyebiliriz. .withBody() ile direkt olarak bir requestBody yazabilirsiniz. Fakat kodun daha okunabilir ve düzenli olması açısından bu json’ı bir dosyadan okuyoruz. Bunu yapabilmek için de .withBodyFile()’ı kullandık. İçine verdiğimiz dosyayı “resources → __files” altında bulabilirsiniz. Aşağıda gördüğünüz gibi bir responseBody’i wireMock sayesinde bize dönecektir.

User user = userService.saveUser(new User("Melih", "Sakarya", 39));
assertNotNull(user.getId());
Assert.assertEquals("Melih",user.getName());

Testimize assertEquals() ile bir assertion ekleyelim. Burada gelen cevaptaki name’in “Melih” olduğunu öne sürüyoruz. Fakat add-user-json’da gördüğünüz üzere bize response.name değeri “Ahmet” olarak gelecektir. Yani testimiz hata alacaktır :

ResponseBody’mizi düzenleyelim ve tekrar çalıştıralım :

Service’den bize dönmesini istediğimiz responseBody’i düzenledik ve artık testimiz başarılı oldu. Bu arada biz test sırasında sadece basePath’i verdik. BaseURL’in nerede olduğunu söylemedik. Bunu iki şekilde verebilirsiniz , ilki :

‘main → resources → application.properties’ den baseURL’i setleyebilirsiniz. İkinci yol olarak ise @ConfigureWireMock annotation’u altında .proxyVia(“localhost”, 8080); şeklinde bir tanımlama yapabilirsiniz.

Şimdi GET request atan bir test’i inceleyelim :

Burada stubFor(get()) ile bir GET request’e response tasarlıyoruz. En altta da .withBodyFile(“users.json”) ile’de bize responseBody olarak users.json’ı dönmesi gerektiğini söylüyoruz. Assertion olarak’da gelen listenin size>0 kontrolünü yapıyoruz. Testimizi çalıştırıyoruz ve başarılı şekilde çalıştığını görüyoruz :

Burada dikkatinizi çekmek istediğim kısım ise farklı. Şimdi öncelikle @Test’in sonuna “Thread.sleep(30000);” ekleyelim. Burada amacım wireMock ile bana yanıt veren bir sunucunun ayakta olduğunu görmektir. Şimdi testi tekrar çalıştıralım , sunucu ayağa kalktıktan sonra 30 saniye içinde browser üzerinden verilen “baseUrl + basePath”e gidelim :

Sunucumuz 30 saniye boyunca ayakta tutulduğu için browser üzerinden de bu listeyi getirebilmiş olduk.

Şimdi 30 saniye ayakta tutmak yerine bunu sürekli ayakta tutalım. Ve gerçek bir service isteğini “record” alalım. Bunu yapabilmek için buradaki linkten yararlanıyor olacağım. Buradaki “Running as a Standalone Process”te anlattıkları üzerinden gidiyor olacağız. İlk olarak wiremock-standalone.jar’ı indirmemizi istiyor.

Ardından terminal üzerinde yukarıda verilen komut ile WireMock sunucusunu ayağa kaldırıyoruz :

Verilen komutla WireMock sunucumu ayağa kaldırıyorum. Default olarak port 8080'de ayağa kalktığını görüyorum. Attığım wiremock linkindeki documentation’da “Command line options” altında port’u değiştirmek vs. gibi çoğu bilgi yer almaktadır. Biz şimdilik default port(8080) üzerinden devam edelim. Bu arada server’ı ayağa kaldırdığımız anda __files ve mappings klasörlerinin oluştuğuna dikkat edelim. Bu klasörleri yazdığımız testlerde de kullanmıştık.

WireMock ayakta iken browser’ımız üzerinden “http://localhost:8080/__admin/recorder/” adresine gittiğinizde sizi şu şekilde bir ekran karşılayacaktır :

Record’a başladıktan sonra attığınız istekleri vb. dinliyor olacaktır. Yukarıda da verdiğim Melih Sakarya’nın Javaee projesi üzerinden servisi ayağa kaldırabilirsiniz(link).

Öncelikle Firefox üzerinden Connection Settings → Manual Proxy configuration’dan proxy ayarını vermemiz gerekiyor :

Şimdi 63.34.88.168/sample-javaee/rest/user/list’e istek attığında WireMock’un onu kaydetmesini bekliyoruz :

İsteğimizi attık. Bu arada bu istek atılırken gerçek service ve WireMock aynı anda ayakta. Stop ile WireMock recorder’ı durduralım :

Gördüğünüz üzere 1 stub’ın mapping’ini yaptı. Otomatik oluşturduğu dosyalar(mappings ve __files) içinde de bunu görebiliriz. Şimdi çalışan gerçek service Down olsa bile WireMock ayakta olduğu için 63.34.88.168/sample-javaee/rest/user/list’e istek atabiliyor olacağız. Yani artık bu service’i sanallaştırdık. Record’dan gelen dosyaları alıp projemizdeki recources altına da atabiliriz :

mappings → kullanici.json dosyası record sonrası oluşan bir dosyadır(ismi kullanici olarak değiştirildi). Yani record sonrası bu dosyaları gelip recources altına yerleştirebiliriz. Böylelikle WireMock sunucumuz “/rest/user/list path’ine istek atıldığında artık ne yapacağını biliyor. Record alındığında bize “body” : … şeklinde uzun bir response body sunuyor. Böyle karışık görünen bir şey istemediğimiz için bunu “bodyFileName” : “users.json” ile değiştiriyoruz. Böylece “/rest/user/list” path’ine istek atıldığında responseBody olarak “users.json” şeklinde sağdaki json’ın dönmesini sağlamış oluyoruz.

Record yapıp mappings oluşturmamız yapmamız ne işe yaradı?

Artık urlPath’i “/rest/user/list” olan url’e istek atıldığında service’imiz ne yapacağını mappings tarafından biliyor. Yani yukarıdaki koddaki stubfor(…) kısmını artık yapmamıza gerek yok. Service’in “/rest/user/list” path’ine istek gelirse ne yapması gerektiğini mappings’te zaten verdik.

Stub kısmını çıkardığımızda kodumuz çok daha okunabilir oldu. Service’de olabilecek her istek için WireMock record yaptıktan sonra artık o service’e ihtiyacımız kalmayacaktır. Gerçek service’de herhangi bir sorun olsa bile bizim isteklerimiz bundan etkilenmeyecektir.

Burada wiremock size sadece json dönüyormuş gibi düşünmeyelim. Bu service soap olabilir ve size xml dönebilir. İstek attığınızda size html bile dönebilir. Hadi bir html deneyelim :

n11.com sitesine gidip Command+S ile site içeriğini html olarak kaydettim. Şimdi WireMock cevabını değiştirelim :

WireMock’un ürettiği mappings klasörü içine anasayfa.json adında bir json koyduk. Ardından “url” : “/anasayfa” verdik. “bodyFileName” kısmını yukarıda indirdiğimiz html dosyasının ismini verdik. Bu arada bu html dosyasını WireMock’un ürettiği __files dosyasının içine koymanız gerekiyor görmesi için. Son olarak headers kısmından “Content-Type”ı “text/html” olarak değiştirdik ve kaydettik. Bu işlemlerden sonra WireMock’u terminate edip java -jar w… şeklinde tekrar ayağa kaldırıyoruz.

GIF’te gördüğünüz üzere manuel olarak bir mapping yaptık. “/anasayfa” path’ine gitmek istediğimizde bize n11.com’un anasayfa html’ini vermesini istedik. “localhost:8080/anasayfa” ya gittiğimizde istediğimiz html’i bize vermiş oldu. Yani özetle WireMock bize sadece json sağlamıyor ; Xml, html vs. de sağlıyor.

Başka bir yazıda görüşmek üzere,

Mutlu günler dilerim :)

Kaynakça :

[1] Melih Sakarya Webinar https://www.youtube.com/watch?v=-vCsSH1mpis

[2] WireMock docs http://wiremock.org/docs/

--

--

Semih Saydam
Semih Saydam

Written by Semih Saydam

QA / DevOps Engineer — Be happy :)

No responses yet