17 tháng 6 năm 2024 Công nghệ thông tin
Mô hình Screenplay là một mẫu thiết kế được sử dụng trong việc kiểm thử phần mềm. Bài viết này sẽ khám phá cách sử dụng mô hình Screenplay để viết các trường hợp thử nghiệm giao diện người dùng web.
Bài viết sẽ bắt đầu bằng việc giới thiệu các khái niệm cơ bản của mô hình Screenplay; tiếp theo, chúng ta sẽ phân tích một kịch bản thử nghiệm cụ thể liên quan đến đăng nhập vào GitHub và tạo Issue trên trang web; cuối cùng, chúng ta sẽ viết một trường hợp thử nghiệm dựa trên khung kiểm thử Serenity BDD tuân thủ mô hình Screenplay với ví dụ về quản lý dự án sử dụng Maven.
Dưới đây là phiên bản của JDK, Maven và Serenity BDD được sử dụng trong ví dụ:
JDK: Amazon Corretto 17.0.8
Maven: 3.9.2
Serenity BDD: 4.1.20
1. Mô hình Screenplay là gì?
Mô hình Screenplay (Screenplay Pattern, hay còn gọi là Mô hình Kịch bản) là một mẫu thiết kế dành cho kiểm thử phần mềm, đặc biệt là kiểm thử chấp nhận. Sử dụng mô hình này giúp tạo ra các kịch bản thử nghiệm dễ bảo trì, có khả năng tái sử dụng cao và dễ đọc hơn. Các khái niệm cốt lõi và nguyên tắc của nó như sau:
1.1 Các khái niệm chính
-
Diễn viên (Actors)
Đại diện cho người dùng hoặc vai trò tương tác với hệ thống đang được kiểm tra. Diễn viên sử dụng “khả năng” (Abilities) để thực hiện các nhiệm vụ, giống như cách người dùng thực sự tương tác với hệ thống. Ví dụ: Một diễn viên có thể là “người dùng” của một trang thương mại điện tử, thực hiện các hoạt động như đăng nhập, tìm kiếm sản phẩm, thêm sản phẩm vào giỏ hàng và thanh toán. -
Nhiệm vụ (Tasks)
Đóng gói các hành động mà diễn viên có thể thực hiện, biểu thị các thao tác hoặc tương tác ở cấp độ logic kinh doanh. Ví dụ: “Tìm kiếm sản phẩm” và “Thêm sản phẩm vào giỏ hàng”. -
Tương tác (Interactions)
Các hành động mà diễn viên thực hiện để hoàn thành nhiệm vụ, thường là những tương tác trực tiếp với giao diện người dùng (UI) hoặc API ở cấp độ thấp hơn. Ví dụ: “Nhấn nút gửi” hoặc “Nhập từ khóa vào ô tìm kiếm”. -
Câu hỏi (Questions)
Được sử dụng để truy vấn trạng thái của ứng dụng hoặc xác minh kết quả. Ví dụ: “Người dùng đã đăng nhập chưa?” hoặc “Trang web có hiển thị kết quả mong muốn không?”
1.2 Nguyên tắc
-
Phân tách mối quan tâm (Separation of Concerns)
Bằng cách phân chia “ai” (diễn viên), “làm gì” (nhiệm vụ) và “làm thế nào” (tương tác), mô hình Screenplay giúp tổ chức mã kiểm thử rõ ràng hơn, tăng tính bảo trì. -
Khả năng tái sử dụng (Reusability)
Các nhiệm vụ và tương tác có thể được tái sử dụng trong các bài kiểm thử khác nhau, giảm thiểu việc lặp lại mã. -
Khả năng đọc hiểu (Readability)
Các bài kiểm thử được viết dưới dạng văn bản tường thuật, giúp dễ hiểu hơn. Chúng thường giống như kịch bản hoặc câu chuyện, cải thiện giao tiếp với các bên liên quan không chuyên kỹ thuật.
Rõ ràng, mô hình Screenplay là một phương pháp mạnh mẽ để viết các bài kiểm thử chấp nhận, đặc biệt phù hợp cho các ứng dụng phức tạp. Nó giúp nâng cao tính tái sử dụng, bảo trì và khả năng đọc hiểu của các trường hợp kiểm thử.
2. Phân tích kịch bản kiểm thử
Kịch bản kiểm thử trong bài viết này là: Đăng nhập vào GitHub và tạo Issue trên trang. Dưới đây là phân tích chi tiết của kịch bản này, ánh xạ các yếu tố của nó vào các phần của mô hình Screenplay:
-
Diễn viên (Actors)
Người dùng sử dụng trình duyệt để “đăng nhập vào GitHub và tạo Issue”, chẳng hạn nhưLarry
. -
Nhiệm vụ (Tasks)
Hai nhiệm vụ chính: “Đăng nhập vào GitHub” và “Tạo Issue”, được thực hiện thông qua các tương tác cụ thể. -
Tương tác (Interactions)
“Nhiệm vụ Đăng nhập vào GitHub” bao gồm các tương tác: mở URL đăng nhập, nhập tên người dùng, nhập mật khẩu, nhấn nút đăng nhập và nhập mã xác thực; “Nhiệm vụ Tạo Issue” bao gồm: mở URL tạo Issue, nhập tiêu đề và nhấn nút gửi. -
Câu hỏi (Questions)
IssueTitle
: Sau khi tạo Issue, lấy tiêu đề trang để so sánh với tiêu đề đã nhập.
Sau khi phân tích xong, chúng ta sẽ sử dụng module Screenplay của khung kiểm thử Serenity BDD để viết mã kiểm thử.
3. Viết mã kiểm thử
3.1 Cấu trúc dự án và phụ thuộc Maven
Dự án kiểm thử sử dụng mô hình Screenplay cho kịch bản “Đăng nhập vào GitHub và tạo Issue” có cấu trúc thư mục như sau:
serenity-bdd-screenplay-ui-test-demo
├─ src/test
│ ├─ java
│ │ └─ com.example.tests
│ │ ├─ tasks
│ │ │ ├─ Login.java
│ │ │ └─ CreateIssue.java
│ │ ├─ questions
│ │ │ └─ IssueTitle.java
│ │ ├─ utils
│ │ │ ├─ GoogleAuthenticatorUtil.java
│ │ │ └─ ConfigUtil.java
│ │ └─ GitHubIssueTest.java
│ └─ resources
│ └─ config.properties
└─ pom.xml
Dự án có ba gói: tasks
, questions
và utils
. Trong đó, gói tasks
tương ứng với nhiệm vụ của mô hình Screenplay; gói questions
tương ứng với câu hỏi; gói utils
chứa các lớp công cụ. Ngoài ra, tập tin GitHubIssueTest.java
là tập tin kiểm thử chuẩn JUnit 5 và là điểm khởi đầu của trường hợp kiểm thử; tập tin resources/config.properties
là tập tin cấu hình, lưu trữ thông tin khóa bí mật cần thiết để đăng nhập vào GitHub.
Dưới đây là danh sách phụ thuộc của dự án:
<dependencies>
<!-- serenity bdd -->
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>${serenity-bdd.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>${serenity-bdd.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-webdriver</artifactId>
<version>${serenity-bdd.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-junit5</artifactId>
<version>${serenity-bdd.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>
<!-- logback -->
<dependency> [nohu.com 56](/post/7864/)
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
<scope>test</scope>
</dependency>
<!-- junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Dự án chủ yếu phụ thuộc vào khung kiểm thử Serenity BDD. Ngoài ra, nó sử dụng gói googleauth
để lấy mã xác thực, logback
để ghi nhật ký và JUnit 5
để thực thi kiểm thử và viết câu lệnh xác nhận.
Cuối cùng, hai plugin được sử dụng: maven-compiler-plugin
chịu trách nhiệm biên dịch dự án và King79 Club Game Bài Đổi Thưởng Uy Tín serenity-maven-plugin
chịu trách nhiệm tạo báo cáo.
3.2 Lớp Task
Lớp Task chịu trách nhiệm gọi các tương tác (hoạt động trên trang) để thực hiện các hoạt động nghiệp vụ.
Lớp Login.java
đóng gói các hoạt động liên quan đến đăng nhập vào GitHub, mã nguồn như sau:
// src/test/java/com/example/tests/tasks/Login.javapackagecom.example.tests.tasks;importcom.example.tests.utils.GoogleAuthenticatorUtil;importnet.serenitybdd.screenplay.Actor;importnet.serenitybdd.screenplay.Task;importnet.serenitybdd.screenplay.actions.Click;importnet.serenitybdd.screenplay.actions.Enter;importnet.serenitybdd.screenplay.actions.Open;importorg.openqa.selenium.By;publicclass LoginimplementsTask{privatestaticfinalStringLOGIN_URL="
Lớp này triển khai giao diện Task của Serenity Screenplay và phương thức performAs()
. Các phần tử trang được định nghĩa dưới dạng thuộc tính, và phương thức performAs()
đại diện cho khả năng của Actor. Trong phương thức performAs()
, phương thức actor.attemptsTo()
được gọi để thực hiện các hoạt động như mở URL, nhập tên người dùng, nhập mật khẩu, nhấn nút đăng nhập và nhập mã xác thực.
Lớp CreateIssue.java
đóng gói các hoạt động liên quan đến việc tạo Issue, mã nguồn như sau:
// src/test/java/com/example/tests/tasks/CreateIssue.javapackagecom.example.tests.tasks;importnet.serenitybdd.screenplay.Actor;importnet.serenitybdd.screenplay.Task;importnet.serenitybdd.screenplay.actions.Click;importnet.serenitybdd.screenplay.actions.Enter;importnet.serenitybdd.screenplay.actions.Open;importorg.openqa.selenium.By;publicclass CreateIssueimplementsTask{privatestaticfinalStringCREATE_ISSUE_URL="/issues/new";privatestaticfinalByINPUT_TITLE_ELEM=By.xpath("//input[@id='issue_title']");privatestaticfinalBySUBMIT_BUTTON=By.xpath("//button[contains(text(), 'Submit new issue')]");privatefinalStringgithubRepoURL;privatefinalStringtitle;publicCreateIssue(StringgithubRepoURL,Stringtitle){this.githubRepoURL=githubRepoURL;this.title=title;}@Overridepublic<TextendsActor>voidperformAs(Tactor){StringcreateIssueURL=githubRepoURL+CREATE_ISSUE_URL;actor.attemptsTo(Open.url(createIssueURL),Enter.theValue(title).into(INPUT_TITLE_ELEM),Click.on(SUBMIT_BUTTON));}}
Lớp này cũng triển khai phương thức performAs()
của giao diện Task. Trong phương thức performAs()
, các hoạt động như mở URL, nhập tiêu đề Issue và nhấn nút gửi được thực hiện.
3.3 Lớp Question
Lớp Question tương ứng với câu hỏi trong mô hình Screenplay, được sử dụng cho câu lệnh xác nhận sau này.
Lớp IssueTitle
được sử dụng để lấy tiêu đề Issue sau khi tạo, mã nguồn như sau:
// src/test/java/com/example/tests/questions/IssueTitle.javapackagecom.example.tests.questions;importnet.serenitybdd.screenplay.Actor;importnet.serenitybdd.screenplay.Question;importnet.serenitybdd.screenplay.questions.page.TheWebPage;publicclass IssueTitleimplementsQuestion<String>{@OverridepublicStringansweredBy(Actoractor){returnTheWebPage.title().answeredBy(actor);}}
Lớp này triển khai giao diện Question
của Screenplay và phương thức answeredBy()
.
3.4 Các lớp công cụ
Dự án sử dụng hai lớp công cụ: ConfigUtil.java
và GoogleAuthenticatorUtil.java
, lần lượt dùng để đọc tệp cấu hình và tạo mã xác thực Google.
Mã nguồn của ConfigUtil.java
như sau:
// src/test/java/com/example/tests/utils/ConfigUtil.javapackagecom.example.tests.utils;importjava.io.IOException;importjava.io.InputStream;importjava.util.Properties;publicclass ConfigUtil{privatestaticfinalStringFILE_NAME="/config.properties";privatestaticfinalPropertiesPROPERTIES=newProperties();static{loadProperties();}privatestaticvoidloadProperties(){try(InputStreamis=ConfigUtil.class.getResourceAsStream(FILE_NAME)){PROPERTIES.load(is);}catch(IOExceptione){thrownewRuntimeException("config.properties load failed",e);}}publicstaticStringgetProperty(Stringkey){returnPROPERTIES.getProperty(key);}}
Mã nguồn của GoogleAuthenticatorUtil.java
như sau:
// src/test/java/com/example/tests/utils/GoogleAuthenticatorUtil.javapackagecom.example.tests.utils;importcom.warrenstrange.googleauth.GoogleAuthenticator;publicclass GoogleAuthenticatorUtil{privatestaticfinalGoogleAuthenticatorauthenticator=newGoogleAuthenticator();publicstaticintgetTotpCode(Stringsecret){returnauthenticator.getTotpPassword(secret);}}
3.5 Lớp kiểm thử đơn vị
Dưới đây là lớp nhập cảnh của trường hợp kiểm thử GitHubIssueTest.java
, mã nguồn như sau:
// src/test/java/com/example/tests/GitHubIssueTest.javapackagecom.example.tests;importcom.example.tests.questions.IssueTitle;importcom.example.tests.tasks.CreateIssue;importcom.example.tests.tasks.Login;importcom.example.tests.utils.ConfigUtil;importnet.serenitybdd.core.Serenity;importnet.serenitybdd.junit5.SerenityJUnit5Extension;importnet.serenitybdd.screenplay.Actor;importnet.serenitybdd.screenplay.annotations.CastMember;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;importorg.junit.jupiter.api.extension.ExtendWith;import staticnet.serenitybdd.screenplay.GivenWhenThen.seeThat;import staticorg.hamcrest.Matchers.startsWith;@ExtendWith(SerenityJUnit5Extension.class)publicclass GitHubIssueTest{@CastMember(name="Larry")privateActorlarry;@Test@DisplayName("Kiểm thử tạo Issue trên GitHub")publicvoidtestIssueCreation(){// Đọc biến từ tệp cấu hìnhStringusername=ConfigUtil.getProperty("GITHUB_USERNAME");Stringpassword=ConfigUtil.getProperty("GITHUB_PASSWORD");Stringsecret=ConfigUtil.getProperty("GITHUB_TOTP_SECRET");StringgithubRepoURL=ConfigUtil.getProperty("GITHUB_REPO_URL");// Tiêu đề IssueStringtitle="Serenity Screenplay UI Test";// Đăng nhập và tạo Issuelarry.attemptsTo(newLogin(username,password,secret),newCreateIssue(githubRepoURL,title));// Kiểm thửSerenity.reportThat("Kiểm tra tiêu đề",()->larry.should(seeThat(newIssueTitle(),startsWith(title))));}}
Lớp này là một lớp kiểm thử đơn vị chuẩn JUnit 5, sử dụng SerenityJUnit5Extension.class
để thực thi. Chúng ta sử dụng chú thích @CastMember(name = "Larry")
để đặt tên diễn viên thực hiện hoạt động là larry
. Trong phương thức kiểm thử testIssueCreation()
, trước tiên chúng ta lấy các biến từ tệp cấu hình; sau đó sử dụng phương thức larry.attemptsTo()
để gọi hai nhiệm vụ Login
và CreateIssue
; cuối cùng, sử dụng phương thức larry.should()
để xác nhận tiêu đề Issue và ghi kết quả vào báo cáo.
3.6 Chạy dự án và xem báo cáo
Chạy GitHubIssueTest.java
trực tiếp trong IntelliJ IDEA hoặc sử dụng lệnh sau để chạy kiểm thử:
mvn clean verify
Sau khi chạy xong, một báo cáo HTML sẽ được tạo ra trong thư mục target/site/serenity
, hiệu ứng như sau:

4. Kết luận
Bài viết đã giới thiệu các khái niệm cơ bản của mô hình Screenplay và minh họa cách sử dụng khung kiểm thử Serenity BDD để viết các trường hợp kiểm thử tuân thủ mô hình Screenplay với kịch bản “Đăng nhập vào GitHub và tạo Issue”. Có thể thấy rằng, việc sử dụng mô hình này làm cho mã nguồn rất gọn gàng, các lớp có trách nhiệm rõ ràng, mang lại khả năng tái sử dụng và đọc hiểu tốt.
Dự án ví dụ đầy đủ đã được đẩy lên GitHub, mời bạn quan tâm hoặc Fork.
[1] Serenity BDD: Your First Screenplay Scenario - [2] Serenity BDD: Screenplay Fundamentals -
#Kiểm thử tự động #Java #Selenium