Cách sử dụng Cucumber Java để thực hiện kiểm thử giao diện người dùng (UI)? - 7win

| Jan 16, 2025 min read

22 tháng 5 năm 2024 Máy tính

Trong bài trước “Cucumber là gì? Làm thế nào để sử dụng Cucumber Java để kiểm thử API?”, chúng tôi đã giới thiệu các khái niệm cơ bản của Cucumber và minh họa cách sử dụng Cucumber Java để kiểm thử API thông qua các ví dụ cụ thể. Bài viết này tiếp tục dựa trên đó, với ví dụ tạo Issue trên GitHub từ trang web, khám phá thêm cách sử dụng Cucumber Java để kiểm thử UI (trình duyệt). Ngôn ngữ triển khai của ví dụ này là Java, công cụ kiểm thử trình duyệt được sử dụng là Selenium, và dự án được quản lý bằng Maven.

Phiên bản JDK, Maven và Cucumber mà ví dụ này sử dụng như sau:

JDK: BellSoft Liberica 17.0.7
Maven: 3.9.2
Cucumber Java: 7.17.0

Kết quả thực hiện khi sử dụng Cucumber Java và Selenium để tạo Issue trên GitHub (bao gồm đăng nhập vào GitHub) như sau: !Hiệu ứng thực hiện khi tạo Issue trên GitHub

1. Cấu trúc dự án và phụ thuộc Maven

Cấu trúc của ví dụ dự án này như sau:

cucumber-ui-test-demo
├─ src/test
│  ├─ java
│  │  └─ com.example.tests
│  │    ├─ stepdefs
│  │    │  ├─ LoginStep.java
│  │    │  └─ CreateIssueStep.java
│  │    ├─ pages
│  │    │  ├─ LoginPage.java
│  │    │  └─ IssuesPage.java
│  │    ├─ utils
│  │    │  ├─ GoogleAuthenticatorUtil.java
│  │    │  └─ WebDriverFactory.java
│  │    │  └─ ConfigUtil.java
│  │    ├─ hooks
│  │    │  └─ ScreenshotHook.java
│  │    └─ TestRunner.java
│  └─ resources
│    ├─ features
│    │  └─ github-issues.feature
│    └─ config.properties
└─ pom.xml

Dưới đây là một lời giải thích ngắn gọn về chức năng của từng gói, thư mục và tệp tin:

  • Gói stepdefs: Gói này được sử dụng để đặt các lớp định nghĩa bước Step Definitions, tức là các thực thi Java tương ứng với các bước được định nghĩa trong tệp đặc điểm của Cucumber. LoginStep.java chịu trách nhiệm thực hiện các bước liên quan đến đăng nhập, và CreateIssueStep.java thực hiện các bước liên quan đến việc tạo Issue. Lưu ý rằng ví dụ này áp dụng thiết kế phân tầng, logic cụ thể để điều khiển trình duyệt bằng Selenium không được thực hiện trực tiếp trong gói này mà được đặt trong gói pages, và các lớp trong gói này chỉ gọi các thực thi tương ứng từ gói pages.
  • Gói pages: Gói này chứa các lớp đối tượng trang (Page Object), chịu trách nhiệm gọi các thao tác Selenium để điều khiển trình duyệt.
  • Gói utils: Gói này chứa các lớp công cụ. GoogleAuthenticatorUtil.java được sử dụng để tạo mã xác thực hai yếu tố của Google Authenticator; WebDriverFactory.java được sử dụng để lấy thống nhất Selenium WebDriver; và ConfigUtil.java được sử dụng để đọc thông tin cấu hình từ tệp cấu hình.
  • Gói hooks: Gói này chứa các móc Cucumber (hook), cho phép thêm logic trước hoặc sau mỗi kịch bản (Scenario) hoặc bước (Step). Lớp ScreenshotHook.java trong gói này chụp ảnh màn hình trang web hiện tại sau mỗi bước trong kịch bản Cucumber và đính kèm vào báo cáo cuối cùng.
  • Tệp TestRunner.java: Lớp này là điểm khởi đầu của dự án kiểm thử.
  • Thư mục resources/features: Thư mục này chứa các tệp đặc điểm của Cucumber.
  • Tệp resources/config.properties: Tệp này là tệp cấu hình của dự án, dùng để lưu tài khoản GitHub, mật khẩu và khóa bí mật cho xác thực hai yếu tố.

Dưới đây là danh sách phụ thuộc mà ví dụ dự án này sử dụng:

<dependencies>
  <!-- cucumber -->
  <dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
  </dependency>
  <!-- selenium -->
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>${selenium.version}</version>
    <scope>test</scope>
  </dependency>
  <!-- google authenticator -->
  <dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId>googleauth</artifactId>
    <version>1.5.0</version>
    <scope>test</scope>
  </dependency>
  <!-- junit -->
  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Như có thể thấy, ngoài việc đưa vào các phụ thuộc chính như JUnit, Cucumber và Selenium, còn có sự xuất hiện của googleauth để sinh mã xác thực hai yếu tố. Ngoài ra, cũng sử dụng plugin maven-cucumber-reporting để tạo báo cáo HTML.

<plugin>
  <groupId>net.masterthought</groupId>
  <artifactId>maven-cucumber-reporting</artifactId>
  <version>5.8.1</version>
</plugin>

Sau khi giới thiệu xong cấu trúc tổng thể của dự án và chức năng của từng thư mục hay gói, dưới đây sẽ lần lượt giới thiệu chức năng của các tập tin hoặc lớp chính của dự án này.

2. Tập tin Đặc điểm

Nội dung của tập tin đặc điểm Cucumber github-issues.feature như sau:

Feature: Kiểm thử UI GitHub Issues
 Scenario: Thêm một Issue mới
  Given Đăng nhập vào GitHub
  When Mở trang Issues và thêm một Issue với tiêu đề "Cucumber UI Test"
  Then Issue được thêm thành công và tiêu đề là "Cucumber UI Test"

Như có thể thấy, tập tin này chỉ định nghĩa một kịch bản duy nhất: Thêm một Issue mới. Bước Given là giai đoạn chuẩn bị, cần đăng nhập vào GitHub trước khi thêm Issue; bước When là giai đoạn hoạt động thực tế, mở trang và thêm một Issue với tiêu đề chỉ định; bước Then 22win casino là giai đoạn xác nhận, đảm bảo rằng Issue đã được thêm thành công và tiêu đề khớp với tiêu đề chỉ định.

3. Lớp Step Definitions

Gói stepdefs được sử dụng để đặt các thực thi tương ứng với các bước trong tập tin đặc điểm. Nội dung của lớp LoginStep.java trong gói này như sau:

package com.example.tests.stepdefs;
import com.example.tests.pages.LoginPage;
import com.example.tests.utils.WebDriverFactory;
import io.cucumber.java.en.Given;

public class LoginStep {
    private final LoginPage loginPage;

    public LoginStep() {
        loginPage = new LoginPage(WebDriverFactory.getWebDriver());
    }

    @Given("Đăng nhập vào GitHub")
    public void login() {
        loginPage.login();
    }
}

Như có thể thấy, logic của lớp này rất đơn giản, chịu trách nhiệm thực hiện phần Given trong tập tin đặc điểm github-issues.feature, tức là tạo một đối tượng LoginPage và gọi phương thức login() của nó để thực hiện đăng nhập.

Nội dung của lớp Step Definition CreateIssueStep.java như sau:

package com.example.tests.stepdefs;
import com.example.tests.pages.IssuesPage;
import com.example.tests.utils.WebDriverFactory;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;

public class CreateIssueStep {
    private final IssuesPage issuesPage;

    public CreateIssueStep() {
        issuesPage = new IssuesPage(WebDriverFactory.getWebDriver());
    }

    @When("Mở trang Issues và thêm một Issue với tiêu đề {string}")
    public void createIssue(String title) {
        // mở trang Issues
        issuesPage.open();
        // tạo Issue
        issuesPage.createIssue(title);
    }

    @Then("Issue được thêm thành công và tiêu đề là {string}")
    public void checkTitle(String title) {
        assertThat(issuesPage.getTitle(), startsWith(title));
    }
}

Logic của lớp này cũng rất đơn giản, chịu trách nhiệm thực hiện phần WhenThen trong tập tin đặc điểm github-issues.feature. Phần When trước tiên gọi phương thức open() của đối tượng IssuesPage để mở trang Issue, sau đó gọi phương thức createIssue() của đối tượng này để tạo Issue. Phần Then chịu trách nhiệm kiểm tra, tức là sau khi hoàn thành việc tạo Issue, tiêu đề của trang phải khớp với tiêu đề chỉ định.

4. Lớp Đối tượng Trang

Gói pages được sử dụng để đặt các lớp đối tượng trang, các lớp này chịu trách nhiệm gọi Selenium để thực hiện các thao tác trình duyệt thực tế. Nội dung của lớp LoginPage.java trong gói này như sau:

package com.example.tests.pages;
import com.example.tests.utils.ConfigUtil;
import com.example.tests.utils.GoogleAuthenticatorUtil;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    private static final String LOGIN_URL = "đường dẫn đăng nhập";
    
    public void login() {
        driver.get(LOGIN_URL);
        // nhập tên người dùng và mật khẩu
        driver.findElement(USERNAME_ELEM).sendKeys(ConfigUtil.getProperty("GITHUB_USERNAME"));
        driver.findElement(PASSWORD_ELEM).sendKeys(ConfigUtil.getProperty("GITHUB_PASSWORD"));
        
        // nhấp nút "Sign in"
        driver.findElement(SIGN_IN_BUTTON).click();
        
        // nhập mã xác thực
        int code = GoogleAuthenticatorUtil.getTotpCode(ConfigUtil.getProperty("GITHUB_TOTP_SECRET"));
        driver.findElement(TOTP_ELEM).sendKeys("" + code);
    }
}

Như có thể thấy, lớp này bao gồm các thuộc tính và hành vi của trang đăng nhập. Các phần tử trang được định nghĩa làm thuộc tính, và phương thức biểu thị hành vi mà trang này cung cấp. Phương thức login() bao gồm toàn bộ hành vi đăng nhập vào GitHub: bao gồm mở trang đăng nhập, nhập tên người dùng và mật khẩu, nhấp nút đăng nhập, và nhập mã xác thực.

Nội dung của lớp IssuesPage như sau:

package com.example.tests.pages;
import com.example.tests.utils.ConfigUtil;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;

public class IssuesPage {
    private static final String ISSUES_URL = "/issues";
    private static final By CREATE_ISSUE_BUTTON = By.partialLinkText("New issue");
    private static final By INPUT_TITLE_ELEM = By.xpath("//input[@id='issue_title']");
    private static final By SUBMIT_BUTTON = By.xpath("//button[contains(text(), 'Submit new issue')]");
    private final WebDriver driver;

    public IssuesPage(WebDriver driver) {
        this.driver = driver; [cách rút tiền fun88 wtf](https://www.jzxdyy.com) 
    }

    public void open() {
        driver.get(ConfigUtil.getProperty("GITHUB_REPO") + ISSUES_URL);
        new WebDriverWait(driver, Duration.ofMinutes(1)).until(ExpectedConditions.elementToBeClickable(CREATE_ISSUE_BUTTON));
    }

    public void createIssue(String title) {
        driver.findElement(CREATE_ISSUE_BUTTON).click();
        new WebDriverWait(driver, Duration.ofMinutes(1)).until(ExpectedConditions.visibilityOfElementLocated(INPUT_TITLE_ELEM));
        driver.findElement(INPUT_TITLE_ELEM).sendKeys(title);
        driver.findElement(SUBMIT_BUTTON).click();
    }

    public String getTitle() {
        return driver.getTitle();
    }
}

Lớp này có ba phương thức: open(), mở trang Issues; createIssue(), tạo Issue; và getTitle(), lấy tiêu đề trang hiện tại. Tất cả đều dựa trên các thao tác của Selenium WebDriver.

5. Lớp Hooks

Cucumber cung cấp nhiều chú thích để thêm logic bổ sung trước hoặc sau khi thực hiện các bước. Ví dụ này sử dụng chú thích @AfterStep để chụp ảnh màn hình sau mỗi bước trong kịch bản.

package com.example.tests.hooks;
import com.example.tests.utils.WebDriverFactory;
import io.cucumber.java.AfterStep;
import io.cucumber.java.Scenario;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

public class ScreenshotHook {
    @AfterStep
    public void attachScreenshot(Scenario scenario) {
        WebDriver driver = WebDriverFactory.getWebDriver();
        byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
        scenario.attach(screenshot, "image/png", driver.getCurrentUrl());
    }
}

Phương thức attachScreenshot() của lớp ScreenshotHook chụp ảnh màn hình sau mỗi bước và đính kèm hình ảnh vào báo cáo cuối cùng tương ứng với bước đó.

6. Lớp Công cụ

Trước đó đã giới thiệu ba lớp công cụ trong gói utils: ConfigUtil.java, GoogleAuthenticatorUtil.javaWebDriverFactory.java, lần lượt được sử dụng để đọc thông tin cấu hình, sinh mã xác thực hai yếu tố và lấy thống nhất Selenium WebDriver.

7. Lớp Điểm Khởi Đầu

Dưới đây là giới thiệu về lớp điểm khởi đầu TestRunner.java của ví dụ dự án này, nội dung của nó như sau:

package com.example.tests;
import com.example.tests.utils.WebDriverFactory;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.AfterClass;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features="src/test/resources/features", plugin={"json:target/cucumber.json"})
public class TestRunner {
    @AfterClass
    public static void closeWebDriver() {
        WebDriverFactory.closeWebDriver();
    }
}

Như có thể thấy, lớp này dựa trên JUnit 5, sử dụng chú thích @RunWith để chỉ định chương trình chạy bằng trình chạy do Cucumber cung cấp. Chú thích @CucumberOptions được sử dụng để chỉ định vị trí của tệp đặc điểm và vị trí của tệp kết quả JSON được tạo ra. Ngoài ra, cần lưu ý rằng lớp này chứa một phương thức closeWebDriver(), được chú thích bằng @AfterClass, nghĩa là đóng trình duyệt sau khi tất cả các trường hợp kiểm thử đã được thực hiện.

8. Chạy Dự Án và Xem Báo Cáo

Do ví dụ dự án này sử dụng trình duyệt Chrome, hãy đảm bảo máy của bạn có trình duyệt Chrome và đã cài đặt ChromeDriver tương ứng (vui lòng tải xuống phiên bản Chrome Driver phù hợp với trình duyệt của bạn từ địa chỉ này và cấu hình đường dẫn giải nén vào biến môi trường hệ thống).

Ví dụ dự án này có thể chạy trực tiếp trong Intellij IDEA hoặc sử dụng lệnh Maven sau để chạy:

mvn clean verify

Kết quả chạy giống như hình động ở đầu bài viết. Sau khi chạy xong, một báo cáo HTML sẽ được tạo ra trong thư mục target/cucumber-report-html. Mở báo cáo này, hiệu ứng như sau: !Báo cáo HTML khi tạo Issue trên GitHub

Như có thể thấy, trạng thái chạy của từng bước trong tệp đặc điểm và ảnh chụp màn hình trang sẽ được hiển thị chi tiết trong báo cáo này.

9. Kết luận

Bài viết này đã sử dụng ví dụ tạo Issue trên GitHub để khám phá cách sử dụng Cucumber Java để kiểm thử UI (trình duyệt). Chủ yếu tập trung vào thiết kế cấu trúc dự án kiểm thử và tích hợp giữa Cucumber và Selenium.

Dự án ví dụ đầy đủ đã được gửi lên GitHub cá nhân của tôi, chào mừng mọi người theo dõi hoặc Fork.

[1] Tài liệu Cucumber: Browser Automation - [2] Lê Lê: Sử dụng cơ bản của Selenium WebDriver -

#Kiểm thử tự động #Cucumber #Java #Selenium