Cucumber’da Stepler Arası Veri Paylaşımı — Scenario Context & Test Context Kavramları
Bu yazımda, Cucumber projelerinde sıkça ihtiyaç duyduğumuz stepler arası veri paylaşımını sağlayan, “test context” ve “scenario context” yapısından bahsedeceğim.
Behaviour-Driven Development destekli bir test aracı olan Cucumber’da senaryo yapısı, art arda çalışan bir dizi step’in bir araya gelmesiyle oluşmaktadır. Senaryodaki her bir step, başka bir step’te kullanmanız veya kontrol etmeniz gereken state veya data bilgisi içerebilmektedir. Bu durum stepler arasında veri paylaşım ihtiyacını ortaya çıkarmaktadır. Benzer şekilde, herhangi bir step definition sınıfında yarattığınız nesneyi, farklı bir sınıfta kullanma ihtiyacınız da olacaktır.
Cucumber’ın bu amaca yönelik desteklediği dependency injection konteyner’larından biri “Pico-Container”dır. Cucumber, sınıflarınızı step definitionları ile birlikte tarayıp onları Pico-Container’a iletir, ardından her senaryo için yeni örnekler oluşturmasını sağlar.
TEST CONTEXT
Peki yukarıda bahsettiğimiz Pico-Container’ı kullanarak, stepler arasında test context paylaşımını nasıl sağlayacağız?
- Maven Projesine Pico-Container’ı ekleme: Maven projesi üzerinde çalışıyorsanız, yapmanız gereken tek şey ilgili dependency’i pom.xml dosyasına eklemek. (Cucumber versiyonu ile aynı olması öneriliyor, diğer versiyonlar için maven repository)
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>1.2.5</version>
</dependency>
2. Test Context sınıfını oluşturma: Test Context’i, projenizde ortak kullanacağınız araçlara (sınıflara) erişebileceğiniz bir aracı olarak düşünebilirsiniz. Örneğin; WebDriver, PageObject, RabbitClient, KafkaClient, IdGenerator gibi farklı step definitionlar’dan erişmek istediğiniz araçları Test Context içinde tutmanız faydalı olacaktır. Böylece kullanmak istediğiniz nesneyi her seferinde “new” ile türetmek yerine, direk bu sınıfı kullanarak erişebileceksiniz.
src/test/java altında “cucumber” isimli bir paket yarattık ve TestContext sınıfını bu paket altında oluşturduktan sonra, constructor içinde kullanacağımız nesneleri initialize ediyoruz ve nesneleri çağırmak için kullanacağımız get methodlarını oluşturuyoruz.
package cucumber;import clients.PageObjectManager;
import clients.WebDriverManager;public class TestContext {
private WebDriverManager webDriverManager;
private PageObjectManager pageObjectManager;
public TestContext(){
webDriverManager = new WebDriverManager();
pageObjectManager = new PageObjectManager(webDriverManager.getDriver());
}
public WebDriverManager getWebDriverManager() {
return webDriverManager;
}
public PageObjectManager getPageObjectManager() {
return pageObjectManager;
}
}
3. Step Definition’da Test Context’i kullanma: Bu adımda, step definition sınıfına constructor ekleyip, TestContext’i bu constructor’a parametre olarak geçtiğimizde, TestContext sınıfından istediğimiz nesneyi bir önceki adımda yazdığımız get methodları ile çağırabilecek durumda oluyoruz. Bir örnek olarak:
package stepDefinitions;import cucumber.TestContext;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.When;
import pageObjects.LoginPage;public class LoginPageSteps {
TestContext testContext;
LoginPage loginPage;public LoginPageSteps(TestContext context) {
testContext = context;
loginPage = testContext.getPageObjectManager().getLoginPage();
}@Given("^user is on the Login Page$")
public void userIsOnTheLoginPage(){
loginPage.navigateToLoginPage();
}@When("^user enters the username \"([^\"]*)\"$")
public void userEntersTheUsername(String username) { loginPage.enterInput(username);
}
}
SCENARIO CONTEXT
Scenario Context ve Test Context kavramlarının birbirine karışmaması için aralarındaki farktan bahsedelim. Yukarıdaki örnekte de belirttiğimiz gibi Test Context, bir testteki farklı stepler arasında bilgilerin paylaşıldığı ana sınıftır. İçinde birçok sınıfa ait nesneyi bulundurabilir. Scenario Context ise, test verilerini özel olarak tuttuğumuz bir sınıftır ve verinin farklı stepler arasında dolaşabilmesi için Test Context’i kullanır.
- Scenario Context sınıfını oluşturma: Önceden yarattığımız “cucumber” paketinin altında ScenarioContext sınıfını oluşturuyoruz. Scenario Context bilgiyi key-value çifti olarak tutmamızı sağlar ve String, Boolean, Integer, Class gibi herhangi bir veri tipini depolamak için istediğiniz sayıda alan oluşturmanızı sağlar. Bir örnek olarak:
package cucumber;import java.util.HashMap;
import java.util.Map;public class ScenarioContext {
private Map<String, Object> scenarioContext;
public ScenarioContext() {
scenarioContext = new HashMap<>();
}
public void setContext(String key, Object value) {
scenarioContext.put(key, value);
}
public Object getContext(String key) {
return scenarioContext.get(key);
}
}
Scenario Context sınıfı veri tipinin ne olduğu ile ilgilenmekten çok o veriyi generic yapıda tutarak, farklı bir step’te kullanma amacına hizmet eder. Burada value’yu Object tipinde tutmamızın sebebi de budur.
2. Scenario Context’i, Test Context sınıfına ekleme: ScenarioContext’i, TestContext sınıfına, önceki objeler ile benzer şekilde eklediğimizde, (Pico-Container kütüphanesi sayesinde) Cucumber stepleri arasında paylaşılmasını sağlamış oluyoruz.
package cucumber;import clients.PageObjectManager;
import clients.WebDriverManager;public class TestContext {
private WebDriverManager webDriverManager;
private PageObjectManager pageObjectManager;
public ScenarioContext scenarioContext; public TestContext(){
webDriverManager = new WebDriverManager();
pageObjectManager = new PageObjectManager(webDriverManager.getDriver());
scenarioContext = new ScenarioContext();
}
public WebDriverManager getWebDriverManager() {
return webDriverManager;
}
public PageObjectManager getPageObjectManager() {
return pageObjectManager;
} public ScenarioContext getScenarioContext() {
return scenarioContext;
}
}
3. Scenario Context’e veri kaydetme: Diyelim ki; LoginPageSteps sınıfında ikinci step’te geçen username bilgisini bir sonraki adımda kontrol edeceğimiz için ScenarioContext’e kaydetmek istiyoruz. Burada artık “setContext” methodunu kullanarak, verimizi aşağıdaki gibi kaydedebiliyoruz:
import cucumber.TestContext;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.When;
import pageObjects.LoginPage;public class LoginPageSteps {
TestContext testContext;
LoginPage loginPage;public LoginPageSteps(TestContext context) {
testContext = context;
loginPage = testContext.getPageObjectManager().getLoginPage();
}@Given("^user is on the Login Page$")
public void userIsOnTheLoginPage(){
loginPage.navigateToLoginPage();
}@When("^user enters the username \"([^\"]*)\"$")
public void userEntersTheUsername(String username) { loginPage.enterInput(username);
testContext.scenarioContext.setContext("name", username);
}
}
4. ScenarioContext’e kaydettiğimiz veriyi kullanma: Diyelim ki; LoginPage’de yukarıda ScenarioContext’e kaydettiğimiz username bilgisi ile HomePage’deki username bilgisini karşılaştırmak istiyoruz. ScenarioContext’teki “getContext” methodu ile verimizi aşağıdaki gibi çağırabiliyoruz. ScenarioContext’te value tipini Object olarak generic tuttuğumuz için, kullanacağımız adımda istediğimiz veri tipine dönüştürmemiz gerekecek.
import org.junit.Assert;import cucumber.TestContext;
import cucumber.api.java.en.Then;
import pageObjects.HomePage;public class HomePageSteps {
TestContext testContext;
HomePage homePage;public HomePageSteps(TestContext context) {
testContext = context;
homePage = testContext.getPageObjectManager().getHomePage();
}@Then("^verify the username on homepage$")
public void verifyTheUsernameOnHomepage(){
String usernameOnLoginPage =(String)testContext.scenarioContext.getContext("username")String usernameOnHomePage = homePage.getUsername();Assert.assertEquals(usernameOnHomePage, usernameOnLoginPage)}
}
Burada basit bir örnek üzerinden Scenario ve Test Context mantığını açıklamış olduk. Gerçek test projelerinde client sayısı ve step definition dosyaları çok daha kompleks yapıda olduğu için, stepler arası client ve veri paylaşım ihtiyacı da buna paralel olarak daha fazla olacaktır.
Özetle; Test Context ve Scenario Context kullanımı sayesinde, stepler arasındaki veri ve araç paylaşımında gereksiz kod kalabalığından kurtulup, kodlarımızı daha basit, anlaşılabilir ve kolay uygulanabilir hale getirmiş oluyoruz.
Kaynaklar: ToolsQA