NỘI DUNG BÀI HỌC

✅Công dụng của Annotations trong TestNG Framework
✅Các Annotations chính trong TestNG Framework
✅Khai báo Annotations trên một Class (không có kế thừa)
✅Khai báo Annotations khi có kế thừa class (Extends)
Cách run test bằng package, class, method trong file suite XML
✅Triển khai Annotations trong code test automation hiện tại

 



✅Công dụng của Annotations trong TestNG Framework

Trong TestNG Framework, annotations được sử dụng để xác định phương thức nào sẽ được chạy trong quá trình kiểm thử và quy định cách thức chạy của chúng. Các annotation này giúp tổ chức, quản lý và điều phối quá trình kiểm thử một cách dễ dàng.

Hiện tại TestNG cung cấp 10 Annotations ở dạng Before và After1 Annotation dạng Test để điều phối sắp xếp việc thực thi test cases.

  • @BeforeSuite/@AfterSuite
  • @BeforeTest/@AfterTest
  • @BeforeGroups/@AfterGroups
  • @BeforeClass/@AfterClass
  • @BeforeMethod/@AfterMethod
  • @Test

Xem thêm:
https://testng.org/#_annotations


Annotation
giúp thêm thông tin vào một đối tượng. Nó có thể được dùng cho class, method, và parameters. TestNG cung cấp rất nhiều loại annotation cho các mục đích khác nhau, trong đó quan trọng có các annotation phục vụ cho mục đích xử lý trước và sau phương thức Test (trước khi chạy test và sau khi chạy test xong).

✳️Vì sao cần xử lý trước và sau phương thức Test?

Trong quá trình kiểm thử tự động với TestNG, việc xử lý trước và sau mỗi phương thức test (@Test) là rất quan trọng để đảm bảo kiểm thử diễn ra chính xác, độc lập và không bị ảnh hưởng bởi trạng thái của các test khác.

Dưới đây là một số lý do chính:

1. Cần thiết lập môi trường trước khi thực hiện test

  • Để đảm bảo mỗi test case được thực thi trong một môi trường đúng và sạch, chúng ta cần chuẩn bị dữ liệu hoặc cấu hình trước khi test chạy.
  • Ví dụ:
    • Mở trình duyệt và điều hướng đến trang web cần kiểm thử.
    • Khởi tạo kết nối với cơ sở dữ liệu hoặc API.
    • Đăng nhập tài khoản trước khi chạy test nếu cần.

Ví dụ:

@BeforeMethod
public void setup() {
    System.out.println("Khởi tạo trình duyệt và đăng nhập vào hệ thống");
}


2. Cần xóa trạng thái sau khi thực hiện test

  • Sau khi test chạy xong, cần dọn dẹp để tránh ảnh hưởng đến các test khác.
  • Ví dụ:
    • Xóa dữ liệu test đã được tạo ra.
    • Đăng xuất tài khoản sau khi test hoàn thành.
    • Đóng trình duyệt để tránh lỗi khi chạy nhiều test liên tiếp.

Ví dụ:

@AfterMethod
public void tearDown() {
    System.out.println("Đóng trình duyệt và xóa dữ liệu test");
}

 

3. Thực hiện các hành động cần thiết nhưng không liên quan đến phương thức test

  • Một số thao tác không ảnh hưởng trực tiếp đến logic test nhưng vẫn rất quan trọng, ví dụ:
    • Chụp ảnh màn hình (screenshot) khi test thất bại để phân tích lỗi.
    • Ghi log hoặc lưu trữ kết quả test để kiểm tra sau này.
    • Xóa session hoặc cache để tránh ảnh hưởng đến các test khác.

Ví dụ:

@AfterMethod
public void takeScreenshotIfTestFails(ITestResult result) {
    if (!result.isSuccess()) {
        System.out.println("Chụp ảnh màn hình do test thất bại");
    }
}

 

✅Các Annotations chính trong TestNG Framework


1. @Test

  • Ý nghĩa: Đánh dấu một phương thức là một test case.
  • Khi nào chạy: Khi chạy bộ kiểm thử (test suite), các phương thức có @Test sẽ được thực thi.
  • Ví dụ:
    @Test
    public void testLogin() {
        System.out.println("Thực hiện kiểm thử đăng nhập");
    }​

     

2. @BeforeSuite

  • Ý nghĩa: Phương thức này chạy trước tất cả các test trong test suite.
  • Khi nào chạy: Trước khi bắt đầu chạy bất kỳ test case nào.
  • Ví dụ:
    @BeforeSuite
    public void beforeSuite() {
        System.out.println("Chạy trước toàn bộ suite");
    }​


3. @AfterSuite

  • Ý nghĩa: Phương thức này chạy sau tất cả các test trong test suite.
  • Khi nào chạy: Sau khi tất cả test case đã hoàn thành.
  • Ví dụ:
    @AfterSuite
    public void afterSuite() {
        System.out.println("Chạy sau toàn bộ suite");
    }​

     

4. @BeforeTest

  • Ý nghĩa: Chạy trước khi thực hiện bất kỳ @Test nào thuộc cùng một <test> trong XML.
  • Khi nào chạy: Trước khi bất kỳ phương thức @Test nào được thực thi trong file XML đó.
  • Ví dụ:
    @BeforeTest
    public void beforeTest() {
        System.out.println("Chạy trước tất cả các test trong một thẻ <test>");
    }​


5. @AfterTest

  • Ý nghĩa: Chạy sau khi tất cả các @Test trong cùng một <test> đã hoàn thành.
  • Khi nào chạy: Sau khi tất cả các phương thức @Test trong <test> của XML đã chạy xong.
  • Ví dụ:
    @AfterTest
    public void afterTest() {
        System.out.println("Chạy sau tất cả các test trong một thẻ <test>");
    }​


6. @BeforeGroups

  • Ý nghĩa: Chạy trước test đầu tiên trong một nhóm (group) được gọi.
  • Khi nào chạy: Trước khi bất kỳ test nào thuộc một nhóm nhất định được thực thi.
  • Ví dụ:
    @BeforeGroups("login")
    public void beforeGroup() {
        System.out.println("Chạy trước nhóm test login");
    }​


7. @AfterGroups

  • Ý nghĩa: Chạy sau test cuối cùng trong một nhóm (group) được gọi.
  • Khi nào chạy: Ngay sau khi test cuối cùng trong nhóm đó hoàn tất.
  • Ví dụ:
    @AfterGroups("login")
    public void afterGroup() {
        System.out.println("Chạy sau nhóm test login");
    }​


8. @BeforeClass

  • Ý nghĩa: Chạy trước khi thực hiện test đầu tiên trong class hiện tại.
  • Khi nào chạy: Sau @BeforeTest, nhưng trước bất kỳ @Test nào trong class đó.
  • Ví dụ:
    @BeforeClass
    public void beforeClass() {
        System.out.println("Chạy trước tất cả các test trong class này");
    }​


9. @AfterClass

  • Ý nghĩa: Chạy sau khi tất cả test trong class hiện tại đã chạy xong.
  • Khi nào chạy: Trước @AfterTest, nhưng sau tất cả @Test trong class đó.
  • Ví dụ:
    @AfterClass
    public void afterClass() {
        System.out.println("Chạy sau tất cả các test trong class này");
    }​


10. @BeforeMethod

  • Ý nghĩa: Chạy trước mỗi test case (@Test).
  • Khi nào chạy: Trước mỗi phương thức @Test trong class.
  • Ví dụ:
    @BeforeMethod
    public void beforeMethod() {
        System.out.println("Chạy trước mỗi phương thức test");
    }​


11. @AfterMethod

  • Ý nghĩa: Chạy sau mỗi test case (@Test).
  • Khi nào chạy: Ngay sau mỗi phương thức @Test trong class.
  • Ví dụ:
    @AfterMethod
    public void afterMethod() {
        System.out.println("Chạy sau mỗi phương thức test");
    }​


Tóm tắt thứ tự thực thi của các annotation trong TestNG

Giả sử chúng ta có một class chứa nhiều test case:

  1. @BeforeSuite
  2. @BeforeTest
  3. @BeforeClass
  4. @BeforeMethod
  5. @Test
  6. @AfterMethod
  7. @BeforeMethod
  8. @Test
  9. @AfterMethod
  10. @AfterClass
  11. @AfterTest
  12. @AfterSuite

Lưu ý:

  • Các annotation giúp kiểm soát quá trình chạy test và sắp xếp chúng theo trình tự hợp lý.
  • @BeforeMethod / @AfterMethod là những annotation được dùng nhiều nhất để thiết lập và dọn dẹp sau mỗi test case.
  • @BeforeClass / @AfterClass hữu ích khi cần setup toàn bộ môi trường cho một class test.
  • @BeforeSuite / @AfterSuite được dùng để cấu hình cấp cao hơn cho toàn bộ test suite.

 

📌 Thứ tự chạy khi bắt đầu (Setup)

Mở từ cấp lớn nhất đến nhỏ nhất:
1️⃣ @BeforeSuite(Thiết lập chung cho toàn bộ suite)
2️⃣ @BeforeTest(Thiết lập chung cho mỗi <test> trong file XML)
3️⃣ @BeforeGroups(Thiết lập chung cho các nhóm test nếu có)
4️⃣ @BeforeClass(Thiết lập trước khi chạy các test trong class)
5️⃣ @BeforeMethod(Thiết lập trước mỗi test case - chạy nhiều lần trước từng @Test)
6️⃣ @Test(Chạy từng test case)

📌 Thứ tự chạy khi kết thúc (Teardown)

Đóng theo thứ tự ngược lại (từ nhỏ đến lớn):
1️⃣ @AfterMethod(Dọn dẹp sau từng test case - chạy nhiều lần sau từng @Test)
2️⃣ @AfterClass(Dọn dẹp sau khi tất cả các test trong class đã chạy xong)
3️⃣ @AfterGroups(Dọn dẹp sau khi nhóm test chạy xong nếu có)
4️⃣ @AfterTest(Dọn dẹp sau khi tất cả các test trong <test> của XML đã hoàn thành)
5️⃣ @AfterSuite(Dọn dẹp sau khi toàn bộ test suite kết thúc)

 

Thứ tự chạy của chúng sẽ được thể hiện qua ví dụ sau.

✅Khai báo Annotations trên một Class (không có kế thừa)

 

public class DemoAnnotation {
     
    @BeforeSuite
    public void beforeSuite() {
        System.out.println("Before Suite");
    }

    @AfterSuite
    public void afterSuite() {
        System.out.println("After Suite");
    }

    @BeforeTest
    public void beforeTest() {
        System.out.println("Before Test");
    }

    @AfterTest
    public void afterTest() {
        System.out.println("After Test");
    }

    @BeforeClass
    public void beforeClass() {
        System.out.println("Before Class");
    }

    @AfterClass
    public void afterClass() {
        System.out.println("After Class");
    }

    @BeforeGroups(groups = { "testOne" })
    public void beforeGroupOne() {
        System.out.println("Before Group testOne");
    }

    @AfterGroups(groups = { "testOne" })
    public void afterGroupOne() {
        System.out.println("After Group testOne");
    }

    @BeforeGroups(groups = { "testTwo" })
    public void beforeGroupTwo() {
        System.out.println("Before Group testTwo");
    }

    @AfterGroups(groups = { "testTwo" })
    public void afterGroupTwo() {
        System.out.println("After Group testTwo");
    }

    @BeforeMethod
    public void beforeMethod() {
        System.out.println("Before Method");
    }

    @AfterMethod
    public void afterMethod() {
        System.out.println("After Method");
    }

    @Test(groups = { "testOne" })
    public void testOneMethod() {
        System.out.println("Test method 1");
    }

    @Test(groups = { "testTwo" })
    public void testTwoMethod() {
        System.out.println("Test method 2");
    }

}​


Và ta cần thêm một config cho file testng.xml

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite" verbose="1">
    <test name="First Test">
        <classes>
            <class name="com.anhtester.DemoAnnotation"></class>
        </classes>
    </test>
</suite>


Và đây là kết quả:

[Selenium Java] Bài 10: Cách sử dụng chú thích (Annotation) trong TestNG Framework | Anh Tester

[Selenium Java] Bài 10: Cách sử dụng chú thích (Annotation) trong TestNG Framework | Anh Tester

Các Groups sẽ có hiệu lực khi chạy nó tại XML file và có gọi tag <groups>

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite" verbose="1">
    <groups>
        <run>
            <include name="testOne"/>
            <!-- include thêm các group khác -->
        </run>
    </groups>
    <test name="Test">
        <classes>
            <class name="com.anhtester.DemoAnnotation"></class>
        </classes>
    </test>
</suite>



[Selenium Java] Bài 10: Cách sử dụng chú thích (Annotation) trong TestNG Framework | Anh Tester


Thứ tự chạy như sau:

Khi bắt đầu: Suite > Test > Group > Class > Method
Khi kết thúc: Method > Class > Group > Test > Suite



✅Khai báo Annotations khi có kế thừa (Extends)

Ở phía trên, chúng ta đã xem về cách sử dụng annotations trên 1 class, nếu chúng ta viết theo dạng POM thì có cả BaseTest, không biết thứ tự run annotations sẽ như thế nào?

Hãy xem ví dụ dưới đây:

Khai báo các annotations bên class cha BaseTest:

public class BaseTest {
     
    @BeforeClass
    public void beforeBaseClass() {
        System.out.println("Parent Before Class method");
    }

    @AfterClass
    public void afterBaseClass() {
        System.out.println("Parent After Class method");
    }

    @BeforeMethod
    public void beforeBaseMethod() {
        System.out.println("Parent Before method");
    }

    @AfterMethod
    public void afterBaseMethod() {
        System.out.println("Parent After method");
    }

}​

Class test cases DemoAnnotation kế thừa lại class BaseTest

import com.anhtester.common.BaseTest;
import org.testng.annotations.*;

public class DemoAnnotation extends BaseTest {

    @BeforeSuite
    public void beforeSuite() {
        System.out.println("Before Suite");
    }

    @AfterSuite
    public void afterSuite() {
        System.out.println("After Suite");
    }

    @BeforeTest
    public void beforeTest() {
        System.out.println("Before Test");
    }

    @AfterTest
    public void afterTest() {
        System.out.println("After Test");
    }

    @BeforeClass
    public void beforeClass() {
        System.out.println("Before Class");
    }

    @AfterClass
    public void afterClass() {
        System.out.println("After Class");
    }

    @BeforeGroups(groups = {"testOne"})
    public void beforeGroupOne() {
        System.out.println("Before Group testOne 1");
    }

    @AfterGroups(groups = {"testOne"})
    public void afterGroupOne() {
        System.out.println("After Group testOne 1");
    }

    @BeforeGroups(groups = {"testTwo"})
    public void beforeGroupTwo() {
        System.out.println("Before Group testTwo 2");
    }

    @AfterGroups(groups = {"testTwo"})
    public void afterGroupTwo() {
        System.out.println("After Group testTwo 2");
    }

    @BeforeMethod
    public void beforeMethod() {
        System.out.println("Before Method");
    }

    @AfterMethod
    public void afterMethod() {
        System.out.println("After Method");
    }

    @Test(groups = {"testOne"}, priority = 1, description = "Đây là test case thứ 1")
    public void testOneMethod() {
        System.out.println("Test method 1");
    }

    @Test(groups = {"testTwo"}, priority = 2)
    public void testTwoMethod() {
        System.out.println("Test method 2");
    }
}

Và file testng.xml như lúc nảy đã chạy:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite" verbose="1">
    <test name="First Test">
        <classes>
            <class name="com.anhtester.DemoAnnotation"></class>
        </classes>
    </test>
</suite>


Đây là kết quả:

[Selenium Java] Bài 10: Cách sử dụng chú thích (Annotation) trong TestNG Framework | Anh Tester


Thứ tự chạy khi có kế thừa (extends) sẽ là:

  • Khi bắt đầu: Parent Before > Child Before
  • Khi kết thúc: Child After > Parent After


Trong một project, không nhất thiết phải sử dụng hết tất cả các annotation này, nhưng ta cần phải biết thứ tự để từ đó điều khiển code của mình chạy theo thứ tự mình mong muốn, ví dụ như chụp screentshot hay screen video record ở cuối mỗi test, khởi tạo connection để đọc file Excel


✅Cách run test bằng package, class, method trong file suite XML

Ví dụ project demo của chúng ta gồm có 3 package như mẫu:

https://github.com/anhtester/TestNGFramework


Trong mỗi Class, ta chỉ cần add 2 Method.

@Test
public void firstTest() {
    System.out.println("First test method");
}

@Test
public void secondTest() {
    System.out.println("Second test method");
}
 

🔆 Cách tạo file suite XML


Lưu ý:

  • File này tên là gì cũng được, không nhất thiết phải là testng.xml, miễn là nó có đuôi .xml là được. Tuy nhiên, nên tránh lấy trùng tên với file config của thằng khác ví dụ như file pom.xml của Maven.
  • File này nằm ở đâu trong Project cũng được, không nhất thiết phải nằm ở ngay dưới folder Project, nhưng tốt nhất là ko nên để nó trong package code nào, nên tách riêng ra.


Nội dung của file .xml:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByClass" verbose="1">
    <test name="FirstTest">
        <classes>
            <class name="com.anhtester.firstpackage.FirstClass"/>
        </classes>
    </test>
</suite>

 
  • Dòng đầu tiên khai báo !DOCTYPE có thể có hoặc không, nhưng nếu bạn ko có thì khi run TestNG sẽ thông báo “It is strongly recommended to add “<!DOCTYPE suite SYSTEM “http://testng.org/testng-1.0.dtd” >” at the top of your file, otherwise TestNG may fail or not work as expected.
  • TestNG bắt buộc phải có khai báo thẻ <suite> và <test>, nếu bạn bỏ 1 trong 2 cái khai báo đó thì TestNG sẽ báo lỗi.  Lưu ý: <test> ở đây không phải là 1 test case, test case là cái khai báo trong class java.
  • Thẻ <suite> và <test> có attribute name, có thể đặt tên tùy ý, không được đặt trùng tên cho các thẻ.
  • Một file TestNG chỉ có 1 thẻ <suite> duy nhất và <suite> có thể có nhiều thẻ <test>.
  • Trong thẻ <test> có thể có 0 hoặc nhiều đối tượng test: <packages>, <classes>, <methods>.


🔆 Cách run test theo Class

Nghĩa là nó sẽ chạy tất cả các @Test nằm trong class chỉ định.

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByClass" verbose="1">
    <test name="FirstTest">
        <classes>
            <class name="com.anhtester.firstpackage.FirstClass"/>
            <class name="com.anhtester.firstpackage.SecondClass"/>
        </classes>
    </test>
</suite>
  • Đối tượng phía trong của <test><classes>
  • Trong <classes> sẽ điền list các class mà ta muốn run test. Name là [packageName].[className]. Ví dụ: firstpackage.FirstClass



Khi run xong, kết quả sẽ hiển thị như sau:


🔆 Cách run test theo package

Nghĩa là nó sẽ chạy tất cả các @Test nằm trong package chỉ định.

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByPackage" verbose="1">
    <test name="FirstTest">
        <packages>
            <package name="com.anhtester.firstpackage"/>
            <package name="com.anhtester.secondpackage"/>
            <package name="com.anhtester.thirdpackage"/>
        </packages>
    </test>
</suite>
  • Đối tượng phía trong của <test><packages>
  • Trong <packages> sẽ điền list các package mà ta muốn run test. Name là [packageName] Ví dụ: firstpackage


LƯU Ý:
chỉ được chọn package sau cùng chứa Class chứ không được gọi package còn chứa package con bên trong.


Khi run xong, kết quả sẽ hiển thị như sau:



Mỗi package có 2 class, mà mỗi class có 2 method test cases, nên 1 package sẽ có 4 test cases. Thì 3 package có 12 test cases.


🔆 Cách run test theo Method

Nghĩa là nó chỉ chạy những @Test nằm trong class và được chỉ định cụ thể từng method.

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByMethod" verbose="1">
    <test name="FirstTest">
        <classes>
            <class name="com.anhtester.firstpackage.FirstClass">
                <methods>
                    <include name="firstTest"/>
                </methods>
            </class>
            <class name="com.anhtester.firstpackage.SecondClass">
                <methods>
                    <include name="firstTest"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>
  • Để run được riêng <method> thì phải có <class>
  • Method muốn được run thì phải sử dụng <include>



Kết quả:


🔆 Cách run test hỗn hợp package, class và method

Nghĩa là nó sẽ chạy tất cả các @Test nằm trong package hoặc class hoặc method cụ thể chỉ định.

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByCommon" verbose="1">
    <test name="FirstTest">
        <packages>
            <package name="com.anhtester.firstpackage"/>
        </packages>
        <classes>
            <class name="com.anhtester.secondpackage.FirstClass">
                <methods>
                    <include name="firstTest"/>
                </methods>
            </class>
            <class name="com.anhtester.thirdpackage.SecondClass"/>
        </classes>
    </test>
</suite>
  • <packages> và <classes> được khai báo cùng cấp, ngang hàng với nhau. <class> không phải khai báo dưới <packge>. Tuy nhiên khai báo <method> thì phải nằm dưới <class>



Đây là kết quả:



🔆 Cách run nhiều module <test> trong suite

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite Run By Module Test" verbose="1">
    <test name="First Test">
        <packages>
            <package name="com.anhtester.firstpackage"/>
        </packages>
    </test>
    <test name="Second Test">
        <classes>
            <class name="com.anhtester.secondpackage.FirstClass"/>
            <class name="com.anhtester.thirdpackage.FirstClass"/>
        </classes>
    </test>
    <test name="Third Test">
        <classes>
            <class name="com.anhtester.secondpackage.SecondClass"/>
            <class name="com.anhtester.thirdpackage.SecondClass"/>
        </classes>
    </test>
</suite>


Kết quả:


Bạn sẽ thấy là TestNG không run theo thứ tự test mà bạn đã config trong TestNG từ firstpackage –> secondpackage –> thirdpackage. Lý do là vì với TestNG thì những thằng nằm dưới <test> là ngang hàng, lúc đó nó chỉ quan tâm đến tên của Class đến tên của phương thức test theo thứ tự bảng chữ cái (Aphabetically).

Và các methods trong 1 Class cũng được run theo thứ tự bảng chữ cái. Đó là lý do vì sao nhiều bạn khi mới học cảm thấy khó chịu vì nó không run theo thứ tự từ trên xuống dưới. Để config run theo thứ tự mong muốn thì mình dùng thuộc tính priority trong @Test.


🔆 Cách sử dụng chức năng Include và Exclude

Include: được dùng trong các trường hợp sau đây:

  • Trong 1 package có nhiều package, nhưng chỉ muốn run số ít package trong đó.
  • Trong 1 Class có nhiều method, chỉ muốn run số ít method (xem lại mục IV ở trên).

Exclude: được dùng trong các trường hợp sau đây:

  • Trong 1 package có nhiều package, nhưng muốn không run một vài package trong đó.
  • Trong 1 Class có nhiều method, nhưng muốn không run một vài method.


Ví dụ:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="SuiteRunByExcludeMethod" verbose="1">
    <test name="FirstTest">
        <classes>
            <class name="com.anhtester.firstpackage.FirstClass">
                <methods>
                    <include name="firstTest"/>
                </methods>
            </class>
            <class name="com.anhtester.secondpackage.SecondClass">
                <methods>
                    <exclude name="firstTest"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>


Đây là kết quả:



NOTE
: trong một project có thể có nhiều file config XML.

 

✅ Annotation @Test

Đây là annotation đánh dấu Method hoặc Class trong TestNG. Nếu nó được đánh dấu cho Class thì tất cả các Methodpublic thì sẽ được run, các Method không phải public sẽ không được run.

Ví dụ:


✅ Các Attribute của Annotation @Test

  • alwaysRun: có giá trị mặc định là false, nó sẽ bị ignore nếu nó không có dependency method (phụ thuộc) . Nếu đặt là true thì Method sẽ được run kể cả khi các dependency method fail.
    VD: @Test(alwaysRun=true)
  • enabled: giá trị mặc định là true. Dùng để đánh dấu method run hoặc không run. Nếu false, method đó sẽ được bỏ qua, không run. Nếu true, method đó sẽ được run.
    VD: @Test(enabled=true)


  • description: dùng để thêm thông tin cho test. Attribute này giúp chúng ta thêm mô tả cho các test case khi mà method Name không thể diễn tả được hết.
    VD: @Test(description="Check permission of Admin role")



  • timeOut: xác định thời gian tối đa mà test có thể run, nếu thời gian run lớn hơn thời gian đã định sẵn thì kết quả test là fail. Đơn vị là mili giây.
    VD: @Test(timeOut=1000)


  • dataProvider: điền data vào cho test method, phục vụ cho data-driven testing (sẽ viết ở bài khác)
  • dataProviderClass: điền tên class mà TestNG sẽ tìm kiếm data-provider method được nhắc đến ở attribute dataProvider. Mặc định thì nó sẽ tìm ở chính class đó hoặc class base. 
  • dependsOnGroups: điền list các group mà test phụ thuộc.
  • dependsOnMethods: điền list các method mà test phụ thuộc. (sẽ viết ở bài khác)


Lưu ý
:

  • Trong 1 @Test có thể áp dụng nhiều annotation cùng với nhau.

Ví dụ:

@Test(enabled=true, description="Anh Tester Automation Testing")
public void TC_03_Multi_Attribute() {
    System.out.println("Anh Tester");
}​

 

✅ Độ ưu tiên để chạy Test trong TestNG

package com.anhtester.annotations;

import org.testng.annotations.Test;

public class Demo_Priority {
    @Test(priority = 2)
    public void test_method_1() {
        System.out.println("Run test method 1");
    }
    @Test(priority = 1)  //độ ưu tiên đầu tiên -> chạy test_method_2() đầu tiên
    public void test_method_2() {
        System.out.println("Run test method 2");
    }
    @Test(priority = 3)
    public void test_method_3() {
        System.out.println("Run test method 3");
    }
}

Đoạn mã set độ ưu tiên sẽ được đặt trong annotation @Test , với cú pháp @Test(priority={expected_order}) (expected_order : 0 .... N , số càng nhỏ thì độ ưu tiên càng lớn).

Trong ví dụ trên thì trình tự thực hiện sẽ là:

  • Run test method 2
  • Run test method 1
  • Run test method 3






✅Triển khai Annotations trong code test automation hiện tại

 

🔆 Thiết lập annotations trong class BaseTest

Chúng ta sẽ dùng các annotation sau trong class BaseTest như sau:

@BeforeSuite dùng để run Appium server, nó sẽ chạy trước 1 lần đầu tiên mỗi suite. Khi nào kết thúc thì tắt thông qua @AfterSuite

@BeforeTest để khởi tạo driver và kết nối đến emulator, này nó sẽ chạy 1 lần để mở app và chạy hết các test cases mới tắt app thông qua @AfterTest


package com.anhtester.common;

import com.anhtester.drivers.DriverManager;
import com.anhtester.helpers.SystemHelpers;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;
import io.appium.java_client.service.local.flags.GeneralServerFlag;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;

public class BaseTest {

    private AppiumDriverLocalService service;
    private String HOST = "127.0.0.1";
    private String PORT = "4723";
    private int TIMEOUT_SERVICE = 60;

    @BeforeSuite
    public void runAppiumServer() {
        //Kill process on port
        SystemHelpers.killProcessOnPort("4723");

        //Build the Appium service
        AppiumServiceBuilder builder = new AppiumServiceBuilder();
        builder.withIPAddress(HOST);
        builder.usingPort(Integer.parseInt(PORT));
        builder.withArgument(GeneralServerFlag.LOG_LEVEL, "info"); // Set log level (optional)
        builder.withTimeout(Duration.ofSeconds(TIMEOUT_SERVICE));

        //Start the server with the builder
        service = AppiumDriverLocalService.buildService(builder);
        service.start();

        if (service.isRunning()) {
            System.out.println("##### Appium server started on " + HOST + ":" + PORT);
        } else {
            System.out.println("Failed to start Appium server.");
        }

    }

    @BeforeTest
    public void setUpDriver() {
        AppiumDriver driver;
        UiAutomator2Options options = new UiAutomator2Options();

        System.out.println("***SERVER ADDRESS: " + HOST);
        System.out.println("***SERVER POST: " + PORT);

        options.setPlatformName("Android");
        options.setPlatformVersion("14");
        options.setAutomationName("UiAutomator2");
        options.setDeviceName("Pixel_9_Pro_XL_API_34");
        options.setAppPackage("com.saucelabs.mydemoapp.android");
        options.setAppActivity("com.saucelabs.mydemoapp.android.view.activities.SplashActivity");
        options.setNoReset(false);
        options.setFullReset(false);

        options.setAutoGrantPermissions(false);

        try {
            driver = new AppiumDriver(new URL("http://" + HOST + ":" + PORT), options);
            DriverManager.setDriver(driver);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }

        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

    }

    @AfterTest
    public void tearDownDriver() {
        if (DriverManager.getDriver() != null) {
            DriverManager.quitDriver();
            System.out.println("##### Driver quit and removed.");
        }
    }

    @AfterSuite
    public void stopAppiumServer() {
        if (service != null && service.isRunning()) {
            service.stop();
            System.out.println("##### Appium server stopped.");
        }
    }

}



Nếu bạn muốn chạy test theo kiểu mỗi test case sẽ mở app và tắt app thì thay thế @BeforeTest và @AfterTest thành @BeforeMethod và @AfterMethod. Kiểu này thì nó hơi tốn thời gian. Tuy nhiên đó cũng là một cách chạy test để kiểm tra app mở nhiều lần xem như thế nào.

🔆 Sử dụng @Test tại class test cases và kế thừa class BaseTest

Tại các class test cases chúng ta sử dụng @Test để khai báo các hàm test cases, một class chứa nhiều @Test và kế thừa lại class BaseTest để các annotations Before và After sẽ khởi chạy tương ứng cho từng class test.

Ví dụ bên dưới là BaseTest đang sử dụng @BeforeTest để setupDriver nên sẽ mở app một lần và chạy 2 test cases liên tiếp nhau.

Test case testAddProductToCart không cần login lại.

package com.anhtester.Bai14_Annotations;

import com.anhtester.common.BaseTest;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.testng.annotations.Test;

public class MyDemoAppTest extends BaseTest {
    @Test(priority = 1)
    public void testLoginSuccess() {
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("View menu")).click();
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Login Menu Item")).click();
        DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).clear();
        DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).sendKeys("bod@example.com");
        DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).clear();
        DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).sendKeys("10203040");
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Tap to login with given credentials")).click();
        MobileUI.sleep(2);

        WebElement title = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("title"));
        if (title.isDisplayed()) {
            System.out.println("Login success.");
        }
    }

    @Test(priority = 2)
    public void testAddProductToCart() {
        //Add product to cart
        DriverManager.getDriver().findElement(AppiumBy.xpath("(//android.widget.ImageView[@content-desc=\"Product Image\"])[1]")).click();
        MobileUI.sleep(1);
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Blue color")).click();
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Increase item quantity")).click();
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Tap to add product to cart")).click();
        MobileUI.sleep(1);
        DriverManager.getDriver().findElement(AppiumBy.id("com.saucelabs.mydemoapp.android:id/cartIV")).click();
        MobileUI.sleep(1);
        String itemTotalInCart = DriverManager.getDriver().findElement(AppiumBy.id("com.saucelabs.mydemoapp.android:id/itemsTV")).getText();
        String priceTotalInCart = DriverManager.getDriver().findElement(AppiumBy.id("com.saucelabs.mydemoapp.android:id/totalPriceTV")).getText();

        if (itemTotalInCart.equals("2") && priceTotalInCart.equals("$ 59.98")) {
            System.out.println("Add product to Cart success.");
        }

    }
}


Nếu BaseTest sử dụng @BeforeMethod để setupDriver thì nó sẽ mở app mỗi lần và chạy 2 test cases độc lập nhau nên khi ấy cần chỉnh test case testAddProductToCart gọi hàm login lại.

Teacher

Teacher

Anh Tester

Software Quality Engineer

Đường dẫu khó chân vẫn cần bước đi
Đời dẫu khổ tâm vẫn cần nghĩ thấu

Cộng đồng Automation Testing Việt Nam:

🌱 Telegram Automation Testing:   Cộng đồng Automation Testing
🌱 
Facebook Group Automation: Cộng đồng Automation Testing Việt Nam
🌱 
Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium
🌱 Telegram
Manual Testing:   Cộng đồng Manual Testing
🌱 
Facebook Group Manual: Cộng đồng Manual Testing Việt Nam

Chia sẻ khóa học lên trang

Bạn có thể đăng khóa học của chính bạn lên trang Anh Tester để kiếm tiền

Danh sách bài học