12 Mart 2023 Pazar

Pact ile CDC Testing

    S.A. Arkadaşlar,
    Araya birkaç teknik olmayan konu aldıktan sonra tekrar teknik bir konuya dönüyoruz. Bugün bahsedeceğimiz konu Consumer Driven Contracts (CDC) testing konusu olacaktır. Mikroservis yapılarda yazdığımız servislerin değişikliklerinde herhangi bir yapıyı bozup bozmaması çok önemli olmaktadır. Burada belirli bir belge(contract) üzerinde anlaşıp el sıkışma olarak düşünebiliriz. Daha sonra yapılan geliştirmeler bu belgeye sadık mı diye bakılır. Eğer bu testler patlamaya başlıyorsa sizin servisinizi tükenten diğer servisler de patlayacak demektir. Bu tarz bir kullanım için çok kullanışlı ve açık kaynaklı olarak geliştirilen Pact kütüphanesini kullanıyor olacağız. Hazırsak başlayalım.

     Daha önceki yazılarıma denk geldiyseniz örnekleri mikroservis projesi üzerinde yapıyoruz. Burada da yine öyle olacaktır. Ayırca değişiklikleri daha ayrıntılı bir şekilde görmek isterseniz de buradaki PR'i inceleyebilirsiniz. Pact kütüphanesinin versiyonları arasında köklü değişiklikler olmuş öncelikle bu konuda dikkatli olmanızı öneririm. Bu yazıyı da yazma sebebi de aslında bu. Kod örneklerini incelerken bunu göz önünde bulundurursanız iyi olur.

    Konuya ilk öncelikle consume (tüketen - Report) eden taraf ile başlayacağız. Daha sonra provider (sağlayıcı - Guide) tarafına değineceğiz. Aslına bakılırsa geliştirme ile ters gidiyormuşuz gibi. Bunun sebebi sağlayıcı tarafındaki kodun testin yazmak, request ve response'ları alarak onu kaydetmek. Bu işlemlerin sonucunda sağlayıcı tarafında bir değişiklik yapıldığında tüketen kısımda hata vermesini bekleyeceğiz.

    Bu örnek için oluşturduğumuz basit bir uç ile başlayalım. 
[HttpGet("getperson/{id}")]
public IActionResult Get(long id)
{
    if (id > 0)
    {
        return new JsonResult(new PersonDto
        {
            Id = 1
        });
    }

    return NoContent();
}
    Şimdi ise bu ucu tüketen testimize bakalım. 
    Testi yazmadan önce constructor içinde ayarları tamamlamamız lazım. Yukarıda da bahsettiğimiz versiyonların farklılığı burada ortaya çıkmıştı. O yüzden bu noktada her yerde kullandığınız versiyonların güncel, aynı ve dotnet core desteklediğine emin olunuz.
public ConsumerPactTests()
{
    var Config = new PactConfig
    {
        //oluşturulacak contract (anlaşma) dosyasının yolu
        PactDir = @"..\..\..\..\..\..\..\Global\pacts",
        DefaultJsonSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        }
    };

    pact = Pact.V3("Consumer", "Provider", Config).WithHttpInteractions();
}
[Fact]
public async Task GetPerson_WhenIdGreaterThanZero_ReturnGuide()
{
    int id = 1;

    pact.UponReceiving("A valid GET request for Guide")
         .Given("There is data")
         .WithRequest(HttpMethod.Get, "/api/guide/getperson/1")
         .WillRespond()
         .WithStatus(HttpStatusCode.OK)
         .WithHeader("Content-Type", "application/json; charset=utf-8")
         .WithJsonBody(new { Id = id });

    // Act & Assert
    await pact.VerifyAsync(async ctx =>
    {
        var response = await ConsumerApiClient.GetPersonUsingGuideApi(id, ctx.MockServerUri);
        var body = await response.Content.ReadAsStringAsync();
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    });
}

    Kabaca yukarıdaki kodları incelemek gerekirse constructor içinde oluşacak antlaşmanın yolu gösterilir ve diğer bazı ayarlar yapılır. Test metodumuz ise yukarıda bahsettiğimiz ucu çağırır ve ona uygun antlaşmanın response ve request kısmını tanımlar. Çağrılacak ucun adresi aldığı parametreleri ve diğer bilgiler ayarlanır. Daha sonra çağrı sonucu gelen cevap kontrol edilir ve beklediğimiz cevapla gelen cevabın aynı olup olmadığı kontrol edilir. Olur da bir değişiklik yapılır veya istediğimiz cevap alınamazsa o zaman da sağlayıcının tarafında değişiklikler olduğunu anlarız.

    Yukarıdaki testi çalıştırdığımızda antlaşma dosyası belirtilen klasör altında oluşmaktadır. Burada dikkat edilmesi gereken nokta ise eğer mac kullanıyorsanız bu dosya oluşmuyor. Daha önce oluşturulduysa herhangi bir sıkıntı olmamaktadır, fakat ilk defa oluşturuyorsanız sizi bu konuda uyarmak isterim. Aynı kod windows makina üzerinde doğru bir şekilde çalışmaktadır. 

    Şimdi de sağlayıcı tarafına geçelim, orası nispeten daha karmaşık ve yine versiyon ile ilgili bazı ayar farklılıkları var. Gerekli tüm kodları repo'dan  alabilirsiniz. Ayrıca kendi resmi dokümanına da göz atabilirsiniz, ama isterlerin sizin projenizle uyuşup uyuşmadığını dikkatle gözden geçirmenizi tavsiye ederim.

    Önce asıl işi yapan test tarafına odaklanalım. Diğer lazım olan dosyaları projeden ya da dokümantasyonundan inceleyebilirsiniz. 

[Fact]
public void EnsureProviderApiHonoursPactWithConsumer()
{
    // Arrange
    var config = new PactVerifierConfig
    {
        Outputters = new List<IOutput>
        {
           new XUnitOutput(_output),
        }
    };

    //Act / Assert
    IPactVerifier pactVerifier = new PactVerifier(config);
    var pactFile = new FileInfo(@"../../../../../../../Global/pacts/consumer-provider.json");
    pactVerifier
        .ServiceProvider("Provide", _fixture.ServerUri)
        .WithFileSource(pactFile)
        .WithProviderStateUrl(new Uri(_fixture.ServerUri, "provider-states"))
        .Verify();
}

    Burada "output" parametresi karşılaşılan hata veya sonuçları ekrana daha anlamlı bir şekilde göstermesi açısından önemli, çünkü hataları bulmak ve onları düzeltmek burası olmadan gerçekten zor hale gelebiliyor. Daha önce oluşturulan anlatşma dosyasını okuyoruz ve bu beklediğimiz sonucu değeri test ediyor mu diye bakıyoruz. Projeyi startup dosyası üzerinden ayağa kaldırmamız lazım. Bunun kısa yolu web application factory ile projeyi ayağa kaldırmak, fakat maalesef pact kütüphanesi bunu yapamayacağımızı söylemiş (kodun biraz aşağısındaki açıklamaya göz atabilirsiniz.), bu yüzden bu işi kendimiz yapmamız lazım o da bu işi biraz uzatmaktadır.  O yüzden bununla ilgili tüm kodlara buradan erişebilirsiniz.

    Bu yazıyı yazarken ciddi şekilde yararlandığım Gökhan Hoca'nın şu yazısına göz atmakta fayda var. Çok daha ayrıntılı bir şekilde anlatılmış, fakat yazının sonundaki yorumda da göreceğiniz üzere güncellemelerde hatalar oluşmaktadır. Ayrıca oradaki örnek .Net framework ile kodlanmış iken (o zamanlarki sürüm bunu destekliyordu) buradaki örnek ise .net 6 ile yazıldı. Biz de bu vesileyle yazıyı kaleme alarak fayda sağlayabileceğini düşündük.

    Yazıyı bir ayet-i kerime ile bitirelim.

    “Şu Kur’an, yakini elde etmiş insanlar için bir basiretler mecmuasıdır, bir hidayet rehberidir.”  (Casiye 45/20) 

Hiç yorum yok:

Yorum Gönder