NỘI DUNG BÀI HỌC

✅ Sử dụng DataProvider trong TestNG để truyền data test
✅ Đọc Excel File vào DataProvider

🔆 DataProvider trong TestNG là gì?

DataProvider giống như một cái nhà máy cung cấp dữ liệu. Bạn có thể tuỳ chỉnh kiểu dữ liệu cũng như số lượng tham số và số lượng dòng dữ liệu để cung cấp cho một test case nào đó.

Trước đây bạn thấy là mình truyền data vào test case là truyền cứng đúng không. Ví dụ "Selenium", "Category" hay email password đều truyền cứng. Mà cái khó là làm sao chúng ta có thể truyền được nhiều bộ dữ liệu khác nhau cho test case khi chạy.

Ví dụ Add Category thì làm sao chúng ta có thể add 2 bộ data hay nhiều bộ data trong một lần chạy?? Khó hen. Đúng là có thể xử lý được cho đến khi học bài Read Excel file. Nhưng chỉ đọc ghi file Excel thông thường thì chưa đủ hay. 

Nên DataProvider sinh ra để giải quyết giúp mình câu chuyện khó khăn về việc truyền data như trên và hay hơn với ghi chú @DataProvider trong TestNG cung cấp.

Nó cũng khó để giải thích nhiều bằng lời nên là chúng ta cứ đi thẳng vào chi tiết code luôn theo cấu trúc của nó sẽ dễ hiểu hơn.

✅ Sử dụng DataProvider trong TestNG


DataProvider Syntax:

@DataProvider (name = "name_of_dataprovider")
public Object[][] methodName() {
    return new Object [][] { values}
}

 

  • TestNG DataProvider (phần chú thích) chứa một thuộc tính để định danh tên của nó là name. Để phân biệt nhiều trình cung cấp dữ liệu với nhau (là @DataProvider á)
  • Nếu chưa chỉ định tên của trình cung cấp dữ liệu, thì tên phương thức sẽ trở thành tên của trình cung cấp dữ liệu đó
  • DataProvider trả về danh sách là mảng dữ liệu 2 chiều của các đối tượng. Chổ này chưa học nhưng cũng dễ thôi. Xíu An chia sẻ.


Nó sẽ hoạt động bằng cách bạn truyền data vào chổ {values} như cấu trúc trên. Và nó sẽ đọc các đối tượng đó là 1 hoặc nhiều dòng dữ liệu với các tham số tương ứng theo mình thiết kế. Tức nhiên nó có quy định và chúng ta học theo tài liệu gốc thôi.

🔆 Làm thế nào để sử dụng DataProvider trong TestNG?

Trước tiên, chúng ta sẽ bắt đầu với một bài kiểm tra DataProvider đơn giản cơ bản. Hãy quan sát đoạn mã sau có chứa @DataProvider.

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class LearnDataProvider {
    @DataProvider(name = "data_provider_01")
    public Object[][] dpMethod() {
        return new Object[][]{{"First-Value"}, {"Second-Value"}};
    }

    @Test(dataProvider = "data_provider_01")
    public void testDataProvider(String value) {
        System.out.println("Passed Parameter is: " + value);
    }
}

Kết quả:

Passed Parameter is: First-Value
Passed Parameter is: Second-Value

===============================================
Default Suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0


Mô tả:

- Đầu tiên chổ {value} thì An truyền 2 giá trị để trong dấu ngoặc riêng {} để phân biệt 2 dòng dữ liệu khác nhau. Và trong mỗi dấu ngoặc thì chỉ có một dữ liệu nên nó tương ứng là chỉ một tham số truyền vào các test case nhận ở mỗi dòng dữ liệu.
- Nếu muốn nhiều tham số thì chúng ta dùng dấu phẩy (,) để phân cách các tham số tương ứng với các dữ liệu truyền vào cách nhau trong cặp dấu ngoặc {}.

Ví dụ: An truyền 3 tham số cho mỗi dòng dữ liệu (mỗi dòng dữ liệu cách nhau dấu ngoặc {} )

    @DataProvider(name = "data_provider_01")
    public Object[][] dpMethod() {
        return new Object[][]{{"Value1", "Value2", "Value3"}, {"Value4", "Value5", "Value6"}};
    }

- Khi đó hàm test case nhận buộc phải có số lượng tham số tương ứng và đúng với kiểu dữ liệu theo thứ tự của DataProvider.

    @Test(dataProvider = "data_provider_01")
    public void testDataProvider(String username, String password, String result) {
        System.out.println("Username is: " + username);
        System.out.println("Password is: " + password);
        System.out.println("Result is: " + result);
    }

- Tên tham số nhận ở test case thì đặt tên gì cũng được, quan trọng là đúng kiểu dữ liệu nhen.

🔆 Cách truyền nhiều tham số trong TestNG DataProviders

Các dòng dữ liệu cách nhau dấu ngoặc {}. Và các tham số trong mỗi dòng dữ liệu sẽ cách nhau dấu phẩy (,)

Dưới đây An tạo DataProvider có 3 tham số cho mỗi dòng như sau:

    @DataProvider(name = "data_provider_02")
    public Object[][] dataHRM() {
        return new Object[][]{{"anhtester", "123456", "Admin"}, {"joe.larson", "joe.larson", "Employee"}};
    }

Thiết kế test case nhận tương ứng với 3 tham số trên:

    @Test(dataProvider = "data_provider_02")
    public void testDataProviderMultiParam(String username, String password, String role) {
        System.out.println("Username is: " + username);
        System.out.println("Password is: " + password);
        System.out.println("Role is: " + role);
    }

Kết quả:

Username is: anhtester
Password is: 123456
Role is: Admin
Username is: joe.larson
Password is: joe.larson
Role is: Employee

===============================================
Default Suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0
===============================================


Nó sẽ chạy 2 lần tuần tự, mỗi lần truyền vào 3 tham số đúng như ý nghĩa mà mình thiết kế DataProvider trên. Ok hen.c

Vậy thì nếu như chúng ta có 10 dòng hay 20 dòng thì sao. Nó cũng sẽ chạy tuần tự thôi. Chổ này chắc khá mất thời gian nhỉ 😄

Yeah TestNG có cung cấp cho chúng ta tính năng chạy song song luôn cho nhanh 😋

🔆 Cách chạy song song (parallel) nhiều dòng dữ liệu trong TestNG DataProviders

Nếu chổ thiết dataprovider mà mình không chỉ rõ gì thêm thì nó sẽ hiểu là chạy tuần tự từng dòng dữ liệu. Và trong TestNG nó cung cấp cho mình thêm một tham số là parallel=true giúp chúng ta có thể chạy được các dòng dữ liệu song song với nhau.
 
Và tức nhiên là chạy song song là test case nào nhận nó sẽ chạy song song tương ứng với số dòng dữ liệu đó.

Chúng ta dùng thuộc tính parallel=true đặt trong ghi chú @DataProvider nào mà cần chạy song song

    @DataProvider(name = "data_provider_02", parallel = true)
    public Object[][] dataHRM() {
        return new Object[][]{{"anhtester", "123456", "Admin"}, {"joe.larson", "joe.larson", "Employee"}};
    }

    @Test(dataProvider = "data_provider_02")
    public void testDataProviderMultiParam(String username, String password, String role) {
        System.out.println("Username is: " + username);
        System.out.println("Password is: " + password);
        System.out.println("Role is: " + role);
    }

[Selenium Java] Bài  29: Sử dụng DataProvider trong TestNG để truyền data test | Anh Tester

Kết quả cũng như trên vì chúng ta chạy data là chuỗi thôi nên chả nhìn thấy được tốc độ và khác biệt mấy. Này xíu mang vào chạy có trình duyệt mới phê. Khởi tạo một lúc 10 cái Chrome cho ngầu nào =))

Khi bình thường mình không chỉ định thì giá trị parallel=false

 

🔆 Truyền DataProvider với Switch Case dùng Reflect Method

Đoạn mã dưới cung cấp một trường hợp chuyển đổi để kiểm tra tên của phương thức và trả về các tham số cùng dòng giá trị theo tên phương thức.


Yêu cầu:
import java.lang.reflect.Method;

@DataProvider (name = "data-provider")
public Object[][] dpMethod (Method m){
    switch (m.getName()) {
	case "Sum": 
	    return new Object[][] {{2, 3 , 5}, {5, 7, 9}};
	case "Diff": 
	    return new Object[][] {{2, 3, -1}, {5, 7, -2}};
	}
    return null;	
}


@Test(dataProvider = "data-provider")
public void Sum(int a, int b, int result) {
    int sum = a + b;
    Assert.assertEquals(result, sum);
}

@Test(dataProvider = "data-provider")
public void Diff(int a, int b, int result) {
    int diff = a - b;
    Assert.assertEquals(result, diff);
}


Kết quả:

[Selenium Java] Bài  29: Sử dụng DataProvider trong TestNG để truyền data test | Anh Tester


Cái Method nó hay chổ là nó tự nhận diện được test case nào dùng thì nó sẽ getName của method đó, này thuộc tính năng reflect của Java. Hơi chuyên code xíu.

Cách này khá hay nè. Về sau các bạn làm chuyên nghiệp rồi thì nên dùng cách này. Mới học thì không nên. Cứ tách ra nhiều @DataProvider trước cho thuần thục đã nhé !!

 

🔆 Cách gọi DataProvider từ một class khác

Như nảy giờ thì chúng ta đặt @DataProvider ở chung class đúng không. Nhưng sau này mình sẽ để nó ở class riêng tiện quản lý và có thể dùng chung cho nhiều test case.

Giờ tạo class mới DataProviderFactory rồi thiết kế các @DataProvider như nảy giờ. Nhớ chú ý phân biệt các tên method khác nhau nhé. Và cả name cũng khác nhau luôn.

import org.testng.annotations.DataProvider;

public class DataProviderFactory {

    @DataProvider(name = "data_provider_02", parallel = true)
    public Object[][] dataHRM() {
        return new Object[][]{{"anhtester", "123456", "Admin"}, {"joe.larson", "joe.larson", "Employee"}};
    }

    @DataProvider(name = "data_provider_03")
    public Object[][] dataLogin() {
        return new Object[][]{{"anhtester", "123456", "Admin"}};
    }
}


Tiếp theo khai báo bên test cases để cho nó đọc hiểu được @DataProvider từ class khác thì cần có thuộc tính dataProviderClass đặt trong @Test() như sau:

import anhtester.com.dataprovider.DataProviderFactory;
import org.testng.annotations.Test;

public class LearnDataProvider {

    @Test(dataProvider = "data_provider_02", dataProviderClass = DataProviderFactory.class)
    public void testDataProviderMultiParam(String username, String password, String role) {
        System.out.println("Username is: " + username);
        System.out.println("Password is: " + password);
        System.out.println("Role is: " + role);
    }

    @Test(dataProvider = "data_provider_03", dataProviderClass = DataProviderFactory.class)
    public void testDataLogin(String username, String password, String role) {
        System.out.println("Username is: " + username);
        System.out.println("Password is: " + password);
        System.out.println("Role is: " + role);
    }
}


Chú ý cái tên class phải chấm chữ class cuối thấy chưa. Nó bắt buộc đó.


✅ Triển khai DataProvider vào project Page Object Model

Giờ lấy cái test case login cho dễ hen. Gọi hàm dataLoginHRM của @DataProvider truyền username, password từ class DataProviderFactory luôn.

Lấy thông tin đăng nhập từ trang HRM: https://app.hrsale.com/

    @DataProvider(name = "data_provider_login")
    public Object[][] dataLoginHRM() {
        return new Object[][]{{"frances.burns", "frances.burns"}};
    }

Gọi lại bên test case LoginTest:

    @Test(priority = 1, dataProvider = "data_provider_login", dataProviderClass = DataProviderFactory.class)
    public void testLoginFromDataProvider(String username, String password) {
        loginPage.logIn(username, password);
        commonPage.dangXuat();
    }

Nhắc lại chú ý cái dataProvider = "data_provider_login" nghĩa là lấy cái name của @DataProvider chứ không phải cái tên của method nhen. Coi chừng nhầm lẫn 😁


Rồi xong rồi đó. Ok hen. Tiếp cái này hay nè. Giờ data nảy giờ là truyền vào cứng dạng String. Bây giờ làm sao lấy từ Excel file 🙌


✅ Đọc data từ Excel file với DataProvider TestNG

Bài hôm trước học rồi phần đọc và ghi file Excel. Giờ chúng ta bổ sung thêm các hàm và class liên quan để xử lý thôi.

Đầu tiên là thêm 2 hàm đọc data hết Sheet và đọc theo từng dòng giới hạn trong class ExcelHelper luôn


🔆 Đọc Excel tất cả các dòng trong một Sheet

public Object[][] getExcelData(String filePath, String sheetName) {
        Object[][] data = null;
        Workbook workbook = null;
        try {
            // load the file
            FileInputStream fis = new FileInputStream(filePath);
            
            // load the workbook
            workbook = new XSSFWorkbook(fis);

            // load the sheet
            Sheet sh = workbook.getSheet(sheetName);

            // load the row
            Row row = sh.getRow(0);

            //
            int noOfRows = sh.getPhysicalNumberOfRows();
            int noOfCols = row.getLastCellNum();

            System.out.println(noOfRows + " - " + noOfCols);

            Cell cell;
            data = new Object[noOfRows - 1][noOfCols];

            //
            for (int i = 1; i < noOfRows; i++) {
                for (int j = 0; j < noOfCols; j++) {
                    row = sh.getRow(i);
                    cell = row.getCell(j);

                    switch (cell.getCellType()) {
                        case STRING:
                            data[i - 1][j] = cell.getStringCellValue();
                            break;
                        case NUMERIC:
                            data[i - 1][j] = String.valueOf(cell.getNumericCellValue());
                            break;
                        case BLANK:
                            data[i - 1][j] = cell.getStringCellValue();
                            break;
                        default:
                            data[i - 1][j] = cell.getStringCellValue();
                            break;
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("The exception is:" + e.getMessage());
            throw new RuntimeException(e);
        }
        return data;
    }


Hàm Excel của mình viết vòng lặp FOR cho chạy từ 1 nên là nó sẽ hiểu từ dòng 1 bỏ dòng tiêu đề ra. (dòng tiêu đề là 0)


Khai báo bên @DataProvider

    @DataProvider(name = "data_provider_login_excel")
    public Object[][] dataLoginHRMFromExcel() {
        ExcelHelper excelHelper = new ExcelHelper();
        Object[][] data = excelHelper.getExcelData(Helpers.getCurrentDir() + "datatest/Login.xlsx", "Sheet1");
        System.out.println("Login Data from Excel: " + data);
        return data;
    }


File Excel như sau:

[Selenium Java] Bài  29: Sử dụng DataProvider trong TestNG để truyền data test | Anh Tester


Gọi DataProvider tại test case LoginTest:


@Test(priority = 1, dataProvider = "data_provider_login_excel", dataProviderClass = DataProviderFactory.class)
public void testLoginFromDataProviderExcel(String username, String password, String result) {
    loginPage.logIn(username, password);
}


Kết quả:

[Selenium Java] Bài  29: Sử dụng DataProvider trong TestNG để truyền data test | Anh Tester

 

🔆 Đọc Excel data với số dòng tuỳ ý sử dụng HashTable

Chổ này thì thực ra custom lại hàm trên cũng được nhưng An muốn chỉ các bạn thêm dạng khác. Còn custom thì các bạn tự nghiên cứu thêm đi. Truyền tham số dòng bắt đầu và dòng kết thúc vào thôi hà. Nào bí An chỉ sau =))

Cái Hashtable thì dùng cho trường hợp nhiều Field trong File Excel. Giả sử bạn có 10 field chả nhẽ bạn tạo 10 tham số tại Test cases 😝

Yeah thế thì An lại custom thêm hàm mới vận dụng Java để xử lý cùng Apache POI.

Giờ các bạn thêm hàm xử lý với Hashtable như sau:

//Hàm này dùng cho trường hợp nhiều Field trong File Excel
public int getColumns() {
    try {
        row = sh.getRow(0);
        return row.getLastCellNum();
    } catch (Exception e) {
        System.out.println(e.getMessage());
        throw (e);
    }
}

//Get last row number (lấy vị trí dòng cuối cùng tính từ 0)
    public int getLastRowNum() {
        return sh.getLastRowNum();
    }

    //Lấy số dòng có data đang sử dụng
    public int getPhysicalNumberOfRows() {
        return sh.getPhysicalNumberOfRows();
    }

    public Object[][] getDataHashTable(String excelPath, String sheetName, int startRow, int endRow) {
        System.out.println("Excel Path: " + excelPath);
        Object[][] data = null;

        try {
            File f = new File(excelPath);
            if (!f.exists()) {
                try {
                    System.out.println("File Excel path not found.");
                    throw new IOException("File Excel path not found.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            fis = new FileInputStream(excelPath);

            wb = new XSSFWorkbook(fis);

            sh = wb.getSheet(sheetName);

            int rows = getLastRowNum();
            int columns = getColumns();

            System.out.println("Row: " + rows + " - Column: " + columns);
            System.out.println("StartRow: " + startRow + " - EndRow: " + endRow);

            data = new Object[(endRow - startRow) + 1][1];
            Hashtable<String, String> table = null;
            for (int rowNums = startRow; rowNums <= endRow; rowNums++) {
                table = new Hashtable<>();
                for (int colNum = 0; colNum < columns; colNum++) {
                    table.put(getCellData(colNum, 0), getCellData(colNum, rowNums));
                }
                data[rowNums - startRow][0] = table;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return data;
    }


Tương tự như trên tạo @DataProvider

@DataProvider(name = "data_provider_login_excel_hashtable")
public Object[][] dataLoginHRMFromExcelHashtable() {
    ExcelHelper excelHelper = new ExcelHelper();
    Object[][] data = excelHelper.getDataHashTable(Helpers.getCurrentDir() + "datatest/Login.xlsx", "Sheet1", 1, 2);
    System.out.println("Login Data from Excel: " + data);
    return data;
}

Chổ này thì cụ thể An đọc từ vị trí dòng 1 đến 2. Mà nó bắt đầu từ 0 là dòng tiêu đề nên vị trí 1 là dòng thứ 2 trong file Excel và vị trí 2 là dòng thứ 3.


Tạo test case mới để gọi dùng

@Test(priority = 1, dataProvider = "data_provider_login_excel_hashtable", dataProviderClass = DataProviderFactory.class)
public void testLoginFromDataProviderExcelHashtable(Hashtable < String, String > data) {
    loginPage.logIn(data.get("username"), data.get("password"));
}


Chú ý chổ truyền vào không còn là tham số riêng lẻ mà lúc này là 1 Hashtable với 2 tham số kiểu String, String

Và truyền xuống các hàm thì lấy đối tượng của Hashtable để chấm gọi theo Key tương ứng với tên tiêu đề của Excel. Lưu ý hoa thường.

Trường hợp hay hơn là mình sẽ truyền cục data đó vào hàm login luôn. Xong trong hàm login thì mới lấy đối tượng Hashtable chấm gọi Key truyền vào các step auto thì nó sẽ hay hơn.

Tham khảo này: truyền Hashtable vào trang Page (hàm signIn ở cuối)

Kết quả:

[Selenium Java] Bài  29: Sử dụng DataProvider trong TestNG để truyền data test | Anh Tester

Hết rồi. Còn mở rộng hay custom gì thêm thì An chia sẻ sau trong các buổi học nhé. Nào đụng vấn đề mới làm chứ nói trước chả nhớ đâu 😜

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