JUnit ve Selenium WebDriver ile Fonksiyonel Test

günü tarafından yayınlandı.

Basit bir CRUD (Create, Read, Update, Delete) işlemi için JUnit ve Selenium WebDriver kullanarak fonksiyonel test nasıl yazılır?

Fonksiyonel test; sistem içerisindeki bir fonksiyonun doğru şekilde çalışıp çalışmadığını kontrol etmektir. Diğer test türleri arasında en geniş kapsamlı olandır. Kullanıcının hareketleri test ortamında oluşturulur.

Fonksiyonel testin en büyük dezavantajı, değişen arayüzlerle birlikte değişmesi gerekliliğidir. Bu maliyetin daha düşük olabilmesi için değişkenleri POM (Page Object Model) nesneleri içerisinde toplarız. Sayfa tasarımları değiştikçe bu nesneler üzerinde değişiklik yapmak test case’ler üzerinde değişiklik yapmaktan daha kolay olacaktır.

functionalTestingEdit.png

Yukarıda gördüğünüz şekilde (sayfa elementleri kırmızı ile ifade edilmiştir, farklı ifadeler için farklı erişim yöntemleri uygulanacaktır) bir düzenleme sayfası için aşağıdaki model oluşturulabilir.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package pageObjects;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class Edit_Page {
  
  private static WebDriver _driver;
  
  public Edit_Page(WebDriver driver){
    _driver = driver;
  }
  
  public WebElement txt_Name(){
    return _driver.findElement(By.id("txtName"));
  }
  
  public WebElement txt_InitialDate(){
    return _driver.findElement(By.id("txtInitialDate"));
  }
  
  public WebElement slc_Company(){
    return _driver.findElement(By.id("listCompany"));
  }
  
  public WebElement btn_Save(){
    return _driver.findElement(By.cssSelector("input[type='submit']"));
  }
  
  public WebElement btn_Cancel(){
    return _driver.findElement(By.linkText("Cancel"));
  }
  
  public WebElement btn_Remove(){
    return _driver.findElement(By.cssSelector("#frmEdit.top > input"));
  }
}

Burada Selenium’un By kütüphanesi ile farklı erişim yöntemlerinin kullanılışını görebilirsiniz. Id’si belirli olan elementlerde doğrudan By.id() ile erişebilirken id atanmamış elementlerde By.cssSelector() uygulayabiliyoruz. Sayfa üzerinde bir adet “Cancel” linki olduğu için de ona doğrudan By.linkText("Cancel") methodu ile erişebiliyoruz.

Dikkat edilmesi gereken diğer bir nokta ise methodların isimlerdirmeleri. Test case’ler içerisinde kullanımı sırasında daha anlamlı hale getirecek ve sayfa üzerinde ki tipini anlayabileceğimiz şekilde isimlendiriyoruz. Yazı veya buton elementleri txt_ veya btn_ önekleriyle başlarken, drop-down list için slc_ önekini tercih ettik.

functionalTestingList.png

Listeleme sayfasında ise sayfa üzerindeki elementlere erişim için aşağıdaki modeli oluşturuyoruz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package pageObjects;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class List_Page {
  
  private static WebDriver _driver;
  
  public List_Page(WebDriver driver){
    _driver = driver;
  }
  
  public void redirect(String baseUrl){
    _driver.get(baseUrl + "/list");
  }
  
  public WebElement btn_Add(){
    return _driver.findElement(By.id("btnAdd"));
  }
  
  public WebElement txt_Search(){
    return _driver.findElement(By.id("txtSearch"));
  }
  
  public WebElement btn_Search(){
    return _driver.findElement(By.id("btnSearch"));
  }
  
  public WebElement btn_Link(String text){
    return _driver.findElement(By.linkText(text));
  }
  
  public WebElement btn_Next(){
    return _driver.findElement(By.xpath("//div[@id='pagination']/ul/li[@class='next']/a"));
  }
  
  public WebElement btn_Previous(){
    return _driver.findElement(By.xpath("//div[@id='pagination']/ul/li[@class='prev']/a"));
  }
}

Önceki örnekten farklı olarak burada Xpath ile erişimi görebilirsiniz. CSS ile erişime oranla daha düşük performanslı olmasına rağmen bazı durumlarda Xpath belirtecini kullanmak gerekir. Bunun için sayfalama linklerine erişimde görebileceğiniz gibi By.xpath() methodunu kullanırız.

Test case içerisinde navigasyonu listeleme sayfasına yönlendirebilmek için redirect methodu yazılmıştır. Dönen sonuç olarak WebElement dönmediği için diğerlerinden ayrılır, bu sebeple isimlendirmesi de farklı olarak sadece işlevini içerir.

Uygulama

Artık hazırlamış olduğumuz bu modelleri test caselerimiz içerisinde kullanabiliriz. Aşağıda veritabanına yeni bir nesne ekleyen, arayan, güncelleyen ve sonunda kaldıran bir senaryo için yazılmış test case’lerini görebilirsiniz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import java.util.concurrent.TimeUnit;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;

//Modellerimizin bulunduğu paket referansı
import pageObjects.*;

//Normal şartlarda testler asenkron çalışır ama bizim senaryomuza göre 
//senkron olarak sırasıyla çalışmaları gerekiyor
//Bunun için isim sıralandırmasını devreye alıyoruz
@FixMethodOrder(MethodSorters.NAME_ASCENDING)     

public class hardwareDatabase {

  public static WebDriver driver;
  public static String testVarName;
  private static String baseUrl;
  
  public static String hardwareName = "testMakinasi"; 
  public static String initialDate = "1995-01-01"; 
  public static String hardwareNameUpdated = "güncellenenTestMakinasi";
  public static String companyName = "Otomatik Mühendis";
  
  public static Edit_Page editPage;
  public static List_Page listPage;

  @BeforeClass
  public static void setUp() throws Exception {
    //Test case'lerden önce tetiklenir, ortam hazırlanır

    //Driver oluşturulur
    driver = new FirefoxDriver();
    baseUrl = "http://hardwareshop.otomatikmuhendis.com/";

    //Driver'ın elementlere erişimi sırasında 10 saniye süre tanınır
    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

    //Sayfaların yüklenmesini için beklenecek süre 200 saniye yapılır
    driver.manage().timeouts().pageLoadTimeout(200, SECONDS);

    //Senaryonun aksamadan aynı pencere üzerinden koşması için driver'ı 
    //modeller ile paylaşıyoruz
    editPage = new Edit_Page(driver);
    listPage = new List_Page(driver);
  }

  @Test
  public void test01addHardware() throws Exception {
    //Nesne ekle

    //Daha önce bahsettiğimiz şekilde ilgili sayfaya yönleniyor
    listPage.redirect(baseUrl);
    
    listPage.btn_Add().click();
    
    //Yazı alanları daima boşaltılarak dolduruluyor
    editPage.txt_Name().clear();
    editPage.txt_Name().sendKeys(hardwareName);
    
    editPage.txt_InitialDate().clear();
    editPage.txt_InitialDate().sendKeys(initialDate);
    
    //Dropdown listesinden seçim yapılıyor
    new Select(editPage.slc_Company()).selectByVisibleText(companyName);
    
    editPage.btn_Save().click();
  }

  @Test
  public void test02searchHardware() throws Exception {
    //Eklediğimiz nesneyi bul

    listPage.txt_Search().clear();
    listPage.txt_Search().sendKeys(hardwareName);
    
    listPage.btn_Search().click();
    
    listPage.btn_Link(hardwareName).click();
    
    editPage.btn_Cancel().click();
  }
  
  @Test
  public void test03updateHardware() throws Exception {
    //Eklediğimiz nesneyi bulup güncelle ve yeni ismiyle tekrar ara

    listPage.txt_Search().clear();
    listPage.txt_Search().sendKeys(hardwareName);
    
    listPage.btn_Search().click();
    
    listPage.btn_Link(hardwareName).click();
    
    editPage.txt_Name().clear();
    editPage.txt_Name().sendKeys(hardwareNameUpdated);
    
    editPage.btn_Save().click();
    
    listPage.txt_Search().clear();
    listPage.txt_Search().sendKeys(hardwareNameUpdated);
    
    listPage.btn_Search().click();
    
    listPage.btn_Link(hardwareNameUpdated).click();
    
    editPage.btn_Cancel().click();
  }

  @Test
  public void test04deleteHardware() throws Exception {
    //Güncellenen nesneyi kaldır

    listPage.txt_Search().clear();
    listPage.txt_Search().sendKeys(hardwareNameUpdated);
    
    listPage.btn_Search().click();
    
    listPage.btn_Link(hardwareNameUpdated).click();
    
    editPage.btn_Remove().click();  
  }

  @Test
  public void test05moveToNextPage() throws Exception {
    //Sonraki sayfaya git

    listPage.btn_Next().click();
  }

  @Test
  public void test06moveToPreviousPage() throws Exception {
    //Önceki sayfaya git

    listPage.btn_Previous().click();
  }
  
  @AfterClass
  public static void tearDown() throws Exception {
    //Tüm testler çalıştıktan sonra tetiklenir

    //Driver'dan çıkılarak, pencerenin kapanması sağlanır
    driver.quit();
  }
}

Bir dahaki fonksiyonel test yazımızda Assertion kullanımını göreceğiz. JUnit Assert methodları ile sayfa içeriğinde yazılanlar beklentilerimizi karşılıyor mu bunun uygulamasını yapacağız.

junit, selenium, functional, testing Testing

Yazar: Olcay Bayram

Çeşitli teknoloji firmalarında çalışmış, yurtiçi ve yurtdışı farklı sektörlerden tecrübe edinmiş, derdini iki yabancı dilde anlatabilen bir yazılım uzmanı.