~/Ali GÖREN

SpecFlow ile BDD(Behavior Driven Development) Gerçekleştirmek

Ali Goren · · 7 dk okuma

SpecFlow ile BDD(Behavior Driven Development)


Herkese selamlar. Bu yazının konusu, BDD üzerine olacak. Yazıda kullanılan araç olan SpecFlow, .NET ile kullanılıyor. Ancak siz cucumber kullanarak yine bu işi gerçekleştirebilirsiniz ya da kullandığınız dilde başka neler var ise.

Başlamadan önce GitHub Repository’si: aligoren/specflow-example: Specflow example (github.com)

Nedir Bu BDD?

BDD kısacası yazılım geliştirme süreçlerinde ortak bir anlayışı ortaya koymak ve var olan farklı domainler arasındaki iş birliğini geliştirmek amacıyla kullanılan bir yaklaşımdır.

BDD’den bahsederken, bir yazılımın nasıl davranacağını (behavior), kabul kriterlerini(acceptance criteria) ve iş gereksinimlerini(business requirements) konuşuruz. Bu sayede tüm stakeholderlar bir araya gelerek ortak bir dil oluştururlar.

BDD için test-driven development’ın bir parçası diyebiliriz.

Temel Amacı Nedir?

BDD’nin temel amacı bir yazılıma dair neler beklendiğinin belirlenmesi, bu beklentilerin anlaşılır bir şekilde ifade edilmesi ve sonrasında ise doğru bir şekilde uygulandığından emin olmaktır.

BDD, gereksinimlerin ve davranışların anlaşılmasını ve takım üyeleri arasındaki işbirliğini artırmak için doğal dil kullanır. Burada doğal dil derken gerçekten bir konuşma dilinden bahsediyoruz. Mesela bir POSTMAN collection’ını takımdaki bir arkadaşa anlatırken;

  • Şu method 2 değer alır. Birisi username, diğeri password
  • Bu ikisini aldıktan sonra login ol
  • Login olunca access token’ı al

gibi bir doğal dilden bahsediyoruz.

Bir Projede BDD Nasıl Uygulanır?

Yukarıda bahsettiğim gibi BDD, test-driven development’ın bir parçasıdır, extension’ıdır. Bunu beslemek için de BDD bir DSL kullanır.

Gherkin dediğimiz bi DSL ile birlikte developerların kullandığı teknik araçları kullanma gereksinimi ortadan kalkar. İşin içerisinde olmasa da azıcık mantık yürütme ile birlikte business’tan bir kişi bu noktada katkı sağlayabilir.

Gherkin Nedir?

Gherkin, Behavior Driven Development (BDD) yaklaşımında kullanılan doğal dil tabanlı bir sözdizimidir. Gherkin, BDD senaryolarını, özellikleri ve kabul kriterlerini yazmak için kullanılan bir dil formatıdır. BDD sürecinde tüm paydaşların anlayabileceği ve katkıda bulunabileceği bir dil sağlamak amacıyla geliştirilmiştir.

Gherkin dosyaları Feature’lar ve bunların içerisinde de senaryolardan oluşur. Örnek bir Gherkin dosyası şöyledir;

Feature: Calculator
 Sum two numbers
 
Scenario: Add two numbers
 Given the first number is 50
 And the second number is 70
 When two numbers are added
 Then the result should be 120

Yukarıda gördüğümüz feature aslında yazılımın davranışınıa dair bize bilgiler sunmaktadır. Dilin keywordlerini şöyle açıklayabiliriz;

  • Feature: Yazılımın bir özelliğini tanımlar.
  • Scenario: Belirli bir davranışı veya işlevselliği tanımlar.
  • Given: Senaryonun başlangıç durumunu veya önkoşullarını belirtir.
  • When: Senaryonun tetikleyici olayını veya eylemini belirtir. X olduğunda vs. gibi bir anlam çıkarabiliriz burada.
  • Then: Senaryonun beklenen sonuçlarını belirtir.
  • And: Birden fazla durumu veya adımı birleştirmek için kullanılır.
  • But: And ifadesinin alternatif bir şeklidir ve bir durumu vurgulamak için kullanılır.

Daha fazla Gherkin için şuraya bakabilirsiniz: Gherkin Language — Use Your Language to Describe Test Cases (specflow.org)

Bir .NET Projesinde BDD Uygulamak

Şimdi gelelim işin somut noktasına. Diyelimki business size bir iş ile geldi. Bu iş karmaşık olabilir de olmayabilir de.

Örneğe göre, ziyaretçiler kalkış ve varış noktasını belirterek uçuş arayacaklar. Sonuca göre de success ya da fail almaları gerekiyor. Product owner hemen senaryoyu yazmaya başlar. Bu senaryoya göre de proje artık ilerleyecektir. Örneğin senaryomuz şöyle olsun

Feature: FlightSearch
 Flight search results
 
Scenario: Define two cities by their codes to search flights
 Given define the first city as ""
 And define the second city as ""
 When two city codes are defined
 Then the result should be ""
 Examples: 
  | from | to  | result        |
  | IST  | ESB | IST-ESB found |
  | IST  | AMS | IST-AMS found |
  | ESB  | CGD | ESB-CGD found |
  | AMS  | BER | AMS-BER not found  |

Product owner kendisine anlatıldığı kadarıyla senaryoyu yazar, değişken olan input ve outputları ise delimiterlar ile tanımlar. Buna göre <from>, <to> ve <result> dinamik değerlere sahip delimiterlar.

Henüz yazılımcı bu noktada devreye girmiyor ve son olarak product owner, buradaki senaryoyu business ve diğer stakeholder yani paydaşlara sunar. Eğer el sıkışılır ise, yazılımcı bu senaryoyu implement eder. Bu noktada, test-driven development devreye girer ve yazılımcı yukarıdaki senaryonun testlerini tek tek yazmaya başlar.

Peki bir BDD framework olsa nasıl olurdu? Kesinlikle faydalı olurdu 😛 Feature’lar için stepler yani Given, When ve Then adımlarını otomatik oluşturan bir framework, developerın da işini kolaylaştırırdı. Bu noktada imdadımıza SpecFlow yetişiyor.

SpecFlow Nedir?

SpecFlow, Behavior Driven Development (BDD) sürecinde Gherkin dilini kullanarak test senaryolarını otomatikleştirmek için kullanılan bir .NET tabanlı açık kaynaklı bir araçtır. SpecFlow, Gherkin dilini kullanarak yazılan senaryoları alır ve bu senaryoları .NET tabanlı bir proje içinde çalışan otomatik testlere dönüştürür.

Kurulum işleri manuel ya da bir template ile gerçekleştirilebilir. Benim size önerim bunu template ile yapmanız.

Welcome to SpecFlow’s documentation! — documentation

Yukarıdaki bağlantı size installation adımlarını sunuyor. VS kullananlar eklentilerden SpecFlow pluginini kurabilirler. Rider da ilk kurulumda bu size soruluyor. Kurmadıysanız da sonradan eklentilerden kurabilirsiniz.

Yukarıdaki görselde kırmızı alan içerisinde proje türümüzü SpecFlow olarak seçiyoruz. Mavi alan içerisinde test framework’ümüzü. Ben burada daha modern bir framework olan xUnit’i seçtim. Yeşil alan içerisinde de FluentAssertions dahil edilsin mi edilmesin mi onu seçiyorum.

Projeyi oluştururken kullandığım IDE Rider olduğu için ekranım böyle ancak aynı türden seçenekleri Visual Studio da size sunmakta. Proje oluşunca karşımıza şöyle bir proje yapısı gelecek.

Proje yeni oluştuğu için Features ve Step içerisinde bir Calculator örneği gelmekte. Bunları silelim. Daha sonrasında Features klasörüne sağ tıklayıp bir feature ekleyelim.

Bu yazının örneğinde FlightSearch.feature olabilir. Bu dosya oluşturulduğunda ve içerisi doldurulduğunda şöyle altı çizili alanlar göreceğiz.

Bu, henüz bu feature’a ait steplerin implement edilmediğini söylüyor. Bir step’in üzerine gelip Create Step diyoruz. Aslında hepsine bunu yapalım😁

Bu işlemi ilk defa yapacağımız için Create new binding class diyoruz

Yeni bir class yaratırken şöyle belirtiyoruz;

Diğerleri için tekrar Create Step yapınca artık oluşturulan definition dosyasını burada görebiliyoruz;

Tüm stepleri implement ettiğimizde oluşturduğumuz C# class’ında şöyle bir kod göreceğiz;

namespace FlightExample.Steps;

[Binding]
public class FlightSearchStepDefinitions
{
    [Given(@"define the first city as ""(.*)""")]
    public void GivenDefineTheFirstCityAs(string from)
    {
        ScenarioContext.StepIsPending();
    }

    [Given(@"define the second city as ""(.*)""")]
    public void GivenDefineTheSecondCityAs(string to)
    {
        ScenarioContext.StepIsPending();
    }

    [When(@"two city codes are defined")]
    public void WhenTwoCityCodesAreDefined()
    {
        ScenarioContext.StepIsPending();
    }

    [Then(@"the result should be ""(.*)""")]
    public void ThenTheResultShouldBe(string result)
    {
        ScenarioContext.StepIsPending();
    }
}

Ayrıca projeye “Build Solution” dediğimizde Feature dosyasını expand edince otomatik bir C# dosyasının generate edildiğini göreceğiz.

Yukarıdaki C# dosyasının içeriğini burada paylaşmayacağım. Ancak data-driven bir yaklaşım da burada yer alıyor 😊

Her neyse, implementasyonlar yapıldı ancak içleri boş durumda şu anda. Bu haldeyken testlerimizi çalıştıralım;

Yukarıdaki hataların sebebi henüz steplerin implement edilmemesinden kaynaklı. Yani testler şu anda fail durumda. Business logic paylaşmayacağım. Ancak aşağıdaki gibi implementasyonlarını gerçekleştirelim. Bu implementasyonların doğruluğunu dikkate almayın şu anlık. Sadece içinin doldurulduğunu görelim.

using FlightSearch.Core;
using FluentAssertions;

namespace FlightExample.Steps;

[Binding]
public class FlightSearchStepDefinitions
{
    private FlightSearch.Core.FlightSearch _flightSearch { get; set; } = new FlightSearch.Core.FlightSearch();
    private FlightQuery _flightQuery { get; set; } = new FlightQuery();

    [Given(@"define the first city as ""(.*)""")]
    public void GivenDefineTheFirstCityAs(string from)
    {
        _flightQuery.From = from;
    }

    [Given(@"define the second city as ""(.*)""")]
    public void GivenDefineTheSecondCityAs(string to)
    {
        _flightQuery.To = to;
    }

    [When(@"two city codes are defined")]
    public void WhenTwoCityCodesAreDefined()
    {
        _flightSearch.CreateFlightQuery(_flightQuery);
    }

    [Then(@"the result should be ""(.*)""")]
    public void ThenTheResultShouldBe(string result)
    {
        var flightResult = _flightSearch.GetFlightResult();
        flightResult.Should().Be(result);
    }
}

Ve testleri tekrar çalıştıralım;

Gördüğünüz gibi testlerimiz otomatik olarak passed durumuna geçti 😊

Evet hepsi bu kadar :) Çok kolaymış değil mi? En azından test case’lerini stakeholder’ın onayını alarak gerçekleştirdiğimiz için eksik bir bakış açısı sorunu yaşamaktan da kaçınabiliriz.

Okuduğunuz için teşekkür ederim. Umarım faydalı olabilmişimdir. Eksik ya da yanlış bir bilgi var ise lütfen bildirin.

Kaynaklar