18 Mayıs 2020 Pazartesi

Integration Test - (.Net Core - xUnit - Web Application Factory)

     S.A. Arkadaşlar,
     Bugün entegrasyon(integration) test konusuna odaklanacağız. Entegrasyon test, birim (unit) testten farklı olarak her bir metodu test etmekten ziyade metodların birbiriyle entegre çalışıp çalışmadığını kontrol eder. Örneğin, birim test yazarken bazı mock'lama objeleri kullanırız. Veri tabanını mock'layıp oradan doğru sonuç dönmüş gibi yaparız, ama entegrasyon test ile "mış" gibi yaptığımız şeyleri gerçekten yapıyoruz. Bunun için de alt yapımızı ayağa kaldırmamız gerekecek. Bunu nasıl mı yapacağız. Buyurun
     Bunu yapmanın her zamanki gibi birden çok yolu var. Biz de bunlardan iki tanesini ele alacağız. Bunlardan bir tanesi IConfgiuration'den üreteceğimiz bir değişken ile bu işi yapmaktır. Açıkçası bir önceki projemizde bu şekilde geliştirme yapmıştık. Bunu yaparken InMemory veya gerçek veri tabanı(yedeği alınmış olması tercih sebebidir) kullanabilirsiniz.

    .Net Core projelerinde Starup.cs içerisinde gerekli ayarlamalar olmaktadır. Burada da benzer şekilde değerlerimizi set etmemiz gerekecektir. Dictionary içersine gerekli ayarlarımızı set etmemiz gerekiyor. Connection string, ulaşmış olduğunuz servisler veya projemizin ayağa kalması için gereken ayarları buraya yazabiliriz.
var dictionaryOfConfiguration = new Dictionary<string, string>();

IConfiguration Configuration = new ConfigurationBuilder()
                .AddInMemoryCollection(dictionaryOfConfiguration)
                .Build();
 
     Bu ayarlamayı yaptıktan sonra ise parametre olarak gereken yerlerde bu oluşturduğumuz kopya ayar değişkenini veriyoruz. Biz bunu yaparken bir üst sınıf (base class) oluşturup diğer tüm testlerimizi buradan kalıtarak çağırdık. Bu şekilde tüm testlerde bunu kolayca kullanabildik.

    Şimdi gelelim diğer yönteme. Yazıyı yazmamıza vesile olan kısım burasıdır. Bu şekilde bir yöntem de geliştirmiş olsak, içimize sinmeyen bir şeyler vardı. Çok geçmeden öğrendik ki, Startup.cs dosyasını bir şekilde ayağa kaldırabiliyorduk. Bunu da sağlayan "WebApplicationFactory" yapısı bulunmaktadır. Böylece yukarıdaki ayarlara gerek kalmadan, Startup.cs içindeki varsayılan(default) ayarlarımız ayağa kalkacaktır.

     Öncelikle nuget üzerinden gerekli paketi indirelim.
Microsoft.AspNetCore.Mvc.Testing
     Paketi yüklediysek artık testimizi bahsettiğimiz yapı ile ayağa kaldırabiliriz.
public class ExampleIntegrationTest: IClassFixture<WebApplicationFactory<NameSpace.Startup>>
{
    private readonly WebApplicationFactory<NameSpace.Startup> _factory;

    public ExampleIntegrationTest(WebApplicationFactory<NameSpace.Startup> factory)
    {
        _factory = factory;
    }
}

     Hangi projedeki Startup.cs dosyasını ayağa kaldırmak istiyorsak onu jenerik(generic) olarak geçiyoruz. Daha sonra Depency Injection ile _factory değişkenimize değeri atıyoruz.

     Şimdi yazacağımız testler içinde artık _factory değişkenini her yerden kullanabiliyoruz. Peki bunu nasıl kullanacağız. Kullanacağımız metodun tipine göre değişir. Post, Get, Delete veya diğer istek tiplerinden birini çağırıyoruz. Bunun için metodumuzun(action) yolunu belirtiyoruz. Örnek bir metod yazarak konuyu daha anlaşılır kılalım.
public class Example
{
    [HttpPost]
    public string SayHi()
    {
       return "Hi Malik";
    }
}
 [Theory]
 [InlineData("/Example/SayHi")]
 public async Task TestGetSupportedLabelFormats(string url)
 {
     var client = _factory.CreateClient();
     using var response = await client.PostAsync(url, null);
     
     //Her hangi bir problem oluşmamışsa true ile yolumuza devam ediyoruz.
     Assert.True(response.IsSuccessStatusCode);
     Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());
     //Oluşturduğumuz client ile işimiz bittiğine göre bellekten siliyoruz.
     client.Dispose();

     var sayHi = await response.Content.ReadAsStringAsync();
     string labels = JsonConvert.DeserializeObject<string>(sayHi);
     string defaultGreeting = "Hi Malik";

     Assert.Equal(defaultGreeting, labels);
     //response yanına using yazdığımızda işi bittiğinde kendisi hafızadan silme işini yapar.
     //Fakat yeni sürüm ile geldiği için her iki şekilde de kullanmak istedik.
     //response.Content.Dispose();
 }

      Bu testi Theory attribute ile imzaladık. Bunun ne anlama geldiğini önceki yazımızda belirtmiştik. Kısaca üzerinden geçmek gerekirse birden fazla metod çağırmamıza olanak sağlar. Böylece SayHi metoduna gideceğimizi söyledik. Bunun Post metodu olduğunu da belirtmiş olduk. Daha sonra buradan olumlu bir cevap gelip gelmediğini test ediyoruz. Bu sizin için yeterli olabilir veya gelen cevabı da karşılaştırmak istiyor olabilirsiniz. Cevabımızı da deserialize edip bunu string olduğunu belirttikten sonra artık çıktımızı da elde etmiştik olduk. Artık beklediğimiz çıktı ile gelen çıktıyı karşılaştırabiliriz.

     Burada değinmek istediğimiz bir diğer nokta ise biz varsayılan olarak WebApplicationFactory kullandık, fakat bunu da düzenleyebiliriz. Bunu için jenerik olarak Startup.cs alan bir yapı oluşturuyoruz. Daha sonra bunu kullanırken da ona göre gerekli değişiklikleri yapmamız gerekiyor. Bahsettiklerimize kısaca bir kod örneği verip yazıyı tamamlayabiliriz. Daha fazla uzamaması adına yazıyı burada sonlandırıyoruz. [Daha fazlası]
public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup: class
{
    //.net core 3.1 ile IWebHostBuilder olduğuna dikkat ediniz. 2.1 IWebBuilder
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
      //gerekli ayarları buraya yazabiliriz.
    }
}
     Unit ve Integration testlerle ilgili daha fazlası için videomu inceleyebilirsiniz.

     Test edilebilir yapılar kurgulamak dileğiyle.

Hiç yorum yok:

Yorum Gönder