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óipages
, và các lớp trong gói này chỉ gọi các thực thi tương ứng từ góipages
. - 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 SeleniumWebDriver
; 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ớpScreenshotHook.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 When
và Then
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.java
và WebDriverFactory.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