NỘI DUNG BÀI HỌC

✅ Thêm thư viện hỗ trợ xử lý JSON file trong Maven project
✅ Xây dựng class xử lý JSON file (đọc/ghi)
✅ Đọc data từ JSON file để làm giá trị config cho device

✅ Thêm thư viện hỗ trợ xử lý JSON file trong Maven project

<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.13.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.18.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.18.3</version>
</dependency>

 

  1. Gson (Google):

    • Chuyển đổi giữa Java Object và JSON (serializedeserialize).

    • Ví dụ: new Gson().toJson(obj) hoặc new Gson().fromJson(json, MyClass.class).

    • Nhẹ, đơn giản, dễ dùng, nhưng ít tính năng nâng cao hơn Jackson.

  2. Jackson Databind (FasterXML):

    • Tương tự Gson, nhưng mạnh hơn về tùy biến, hiệu suất và hỗ trợ sâu.

    • Dùng để ánh xạ giữa JSON ↔ Java object.

    • Ví dụ: new ObjectMapper().writeValueAsString(obj) hoặc readValue(...).

  3. Jackson Core:

    • Là lõi nền tảng JSON parser/streamer cho jackson-databind.

    • Dùng khi cần thao tác trực tiếp với JSON dạng stream (ít phụ thuộc, hiệu suất cao hơn).


Nếu chỉ cần thao tác đơn giản thì Gson là đủ. Nhưng nếu muốn xử lý linh hoạt, nhiều cấu hình (như custom deserializer, ignore field, annotation…), nên dùng Jackson.

Thầy An chơi luôn 2 thư viện 😝

✅ Xây dựng class xử lý JSON file (đọc/ghi)

Cũng tương tự như việc đọc ghi file Properties, các bạn tạo class tên JsonHelpers để chung package helpers để khai báo các hàm đọc ghi JSON như bên dưới:

package com.anhtester.helpers;

import com.anhtester.constants.ConfigData;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class JsonHelpers {

    // Get value from JSON file with multiple keys
    public static String getValueJsonObject(String... keys) {
        String value = null;
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            JsonNode node = objectMapper.readTree(new File(ConfigData.JSON_DATA_FILE_PATH));

            for (String key : keys) {
                node = node.path(key);
            }

            value = node.asText();
            System.out.println("Value: " + value);

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

        return value;
    }

    // Get value from JSON file in new file path with multiple keys
    public static String getValueJsonObject_FilePath(String filePath, String... keys) {
        String value = null;
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            JsonNode node = objectMapper.readTree(new File(filePath));

            for (String key : keys) {
                node = node.path(key);
            }

            value = node.asText();
            System.out.println("Value: " + value);

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

        return value;
    }

    // Get value from JSON array
    public static String getValueJsonArray(int itemIndex, String... keys) {
        String value = null;
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            JsonNode rootNode = objectMapper.readTree(new File(ConfigData.JSON_DATA_FILE_PATH));

            // Lấy item tại index từ mảng gốc
            JsonNode itemNode = rootNode.get(itemIndex);
            if (itemNode == null || !itemNode.isObject()) {
                throw new IllegalArgumentException("Item index không hợp lệ hoặc không phải object.");
            }

            JsonNode current = itemNode;
            for (String key : keys) {
                current = current.path(key);
            }

            value = current.asText();
            System.out.println("Value: " + value);

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

        return value;
    }

    // Get value from JSON array in new file path
    public static String getValueJsonArray_FilePath(String filePath, int itemIndex, String... keys) {
        String value = null;
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            JsonNode rootNode = objectMapper.readTree(new File(filePath));

            // Lấy item tại index từ mảng gốc
            JsonNode itemNode = rootNode.get(itemIndex);
            if (itemNode == null || !itemNode.isObject()) {
                throw new IllegalArgumentException("Item index không hợp lệ hoặc không phải object.");
            }

            JsonNode current = itemNode;
            for (String key : keys) {
                current = current.path(key);
            }

            value = current.asText();
            System.out.println("Value: " + value);

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

        return value;
    }

    public static void updateValueJsonObject(String keyName, Number value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(ConfigData.JSON_DATA_FILE_PATH));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            //Update value if exist key
            jsonObject.addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(ConfigData.JSON_DATA_FILE_PATH);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject(String keyName, String value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(ConfigData.JSON_DATA_FILE_PATH));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            if (jsonObject.has(keyName)) {
                System.out.println("🔁 Update key: '" + keyName + "'");
            } else {
                System.out.println("➕ Add new key: '" + keyName + "'");
            }

            //Update value if exist key
            jsonObject.addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(ConfigData.JSON_DATA_FILE_PATH);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject(String parentKey, String keyName, Number value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(ConfigData.JSON_DATA_FILE_PATH));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo parentKey tồn tại
            if (!jsonObject.has(parentKey) || !jsonObject.get(parentKey).isJsonObject()) {
                jsonObject.add(parentKey, new JsonObject());
            }

            //Update value if exist key
            jsonObject.getAsJsonObject(parentKey).addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(ConfigData.JSON_DATA_FILE_PATH);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject(String parentKey, String keyName, String value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(ConfigData.JSON_DATA_FILE_PATH));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo parentKey tồn tại
            if (!jsonObject.has(parentKey) || !jsonObject.get(parentKey).isJsonObject()) {
                jsonObject.add(parentKey, new JsonObject());
            }

            // Gán keyName vào object con
            jsonObject.getAsJsonObject(parentKey).addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(ConfigData.JSON_DATA_FILE_PATH);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject_FilePath(String filePath, String keyName, String value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(filePath));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo keyName tồn tại
            if (!jsonObject.has(keyName) || !jsonObject.get(keyName).isJsonObject()) {
                jsonObject.add(keyName, new JsonObject());
            }

            //Update value if exist key
            jsonObject.addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to new file
            File jsonFile = new File(filePath);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();

            //Close reader
            reader.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject_FilePath(String filePath, String keyName, Number value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(filePath));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo parentKey tồn tại
            if (!jsonObject.has(keyName) || !jsonObject.get(keyName).isJsonObject()) {
                jsonObject.add(keyName, new JsonObject());
            }

            //Update value if exist key
            jsonObject.addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to new file
            File jsonFile = new File(filePath);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();

            //Close reader
            reader.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject_FilePath(String filePath, String parentKey, String keyName, String value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(filePath));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo parentKey tồn tại
            if (!jsonObject.has(parentKey) || !jsonObject.get(parentKey).isJsonObject()) {
                jsonObject.add(parentKey, new JsonObject());
            }

            //Update value if exist key
            jsonObject.getAsJsonObject(parentKey).addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(filePath);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonObject_FilePath(String filePath, String parentKey, String keyName, Number value) {
        Reader reader;
        try {
            reader = Files.newBufferedReader(Paths.get(filePath));

            Gson gson = new Gson();
            //Convert Json file to Json Object
            JsonObject jsonObject = gson.fromJson(reader, JsonObject.class);
            System.out.println("Original JSON: " + jsonObject);

            // Đảm bảo parentKey tồn tại
            if (!jsonObject.has(parentKey) || !jsonObject.get(parentKey).isJsonObject()) {
                jsonObject.add(parentKey, new JsonObject());
            }

            //Update value if exist key
            jsonObject.getAsJsonObject(parentKey).addProperty(keyName, value);

            System.out.println("Modified JSON: " + jsonObject);

            //Store new Json data to file
            File jsonFile = new File(filePath);
            OutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(gson.toJson(jsonObject).getBytes());
            outputStream.flush();
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonArray(String value, int index, String... keys) {
        if (keys == null || keys.length == 0) {
            throw new IllegalArgumentException("Phải cung cấp ít nhất một Key");
        }

        try (Reader reader = Files.newBufferedReader(Paths.get(ConfigData.JSON_DATA_FILE_PATH))) {
            Gson gson = new Gson();
            JsonArray jsonArray = gson.fromJson(reader, JsonArray.class);

            // Kiểm tra index hợp lệ
            if (index < 0 || index >= jsonArray.size()) {
                throw new IndexOutOfBoundsException("Tham số index nằm ngoài giới hạn.");
            }

            JsonObject current = jsonArray.get(index).getAsJsonObject();

            // Duyệt từ key[0] đến key[n-2], tạo JsonObject nếu chưa có
            for (int i = 0; i < keys.length - 1; i++) {
                String key = keys[i];
                if (!current.has(key) || !current.get(key).isJsonObject()) {
                    current.add(key, new JsonObject());
                }
                current = current.getAsJsonObject(key);
            }

            // Gán giá trị cho key cuối
            current.addProperty(keys[keys.length - 1], value);

            // Ghi lại file
            try (OutputStream outputStream = new FileOutputStream(ConfigData.JSON_DATA_FILE_PATH)) {
                outputStream.write(gson.toJson(jsonArray).getBytes());
                outputStream.flush();
            }

            System.out.println("✅ Updated index " + index + ": " + String.join(" → ", keys) + " = " + value);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void updateValueJsonArray_FilePath(String filePath, String value, int index, String... keys) {
        if (keys == null || keys.length == 0) {
            throw new IllegalArgumentException("Phải cung cấp ít nhất một Key");
        }

        try (Reader reader = Files.newBufferedReader(Paths.get(filePath))) {
            Gson gson = new Gson();
            JsonArray jsonArray = gson.fromJson(reader, JsonArray.class);

            // Kiểm tra index hợp lệ
            if (index < 0 || index >= jsonArray.size()) {
                throw new IndexOutOfBoundsException("Tham số index nằm ngoài giới hạn.");
            }

            JsonObject current = jsonArray.get(index).getAsJsonObject();

            // Duyệt từ key[0] đến key[n-2], tạo JsonObject nếu chưa có
            for (int i = 0; i < keys.length - 1; i++) {
                String key = keys[i];
                if (!current.has(key) || !current.get(key).isJsonObject()) {
                    current.add(key, new JsonObject());
                }
                current = current.getAsJsonObject(key);
            }

            // Gán giá trị cho key cuối
            current.addProperty(keys[keys.length - 1], value);

            // Ghi lại file
            try (OutputStream outputStream = new FileOutputStream(filePath)) {
                outputStream.write(gson.toJson(jsonArray).getBytes());
                outputStream.flush();
            }

            System.out.println("✅ Updated index " + index + ": " + String.join(" → ", keys) + " = " + value);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}


Khai báo các key đường dẫn trỏ đến từng file trong config.properties và khai báo biến vào class ConfigData. (ConfigData.JSON_DATA_FILE_PATH)

 

🔆 Đọc data từ JSON file

Tạo file data.json theo đường dẫn src/test/resources/test_data/data.json và khai báo data cho các tất cả các test cases theo từng module hoặc mỗi file json cho một module khác nhau.

Ví dụ khai báo JSON object như sau:

{
   "platform": "Android",
   "device": "Pixel 8",
   "account": {
      "username": "admin",
      "password": "admin"
   },
   "table": {
      "table_name": "Table 1",
      "hidden_table": "false"
   },
   "product": {
      "product_name": "Product 1",
      "product_price": "1000000",
      "hidden_product": "false",
      "product_image": "src/test/resources/test_data/product1.png"
   }
}


Ví dụ khai báo JSON array như sau:

src/test/resources/test_data/sample_products.json

[
    {
        "id": 1,
        "name": "Laptop Dell XPS 13",
        "specs": {
            "cpu": "Intel i7",
            "ram": "16GB",
            "storage": "512GB SSD"
        },
        "price": {
            "amount": 29990000,
            "currency": "VND"
        },
        "available": true
    },
    {
        "id": 2,
        "name": "MacBook Pro M2",
        "specs": {
            "cpu": "Apple M2",
            "ram": "16GB",
            "storage": "1TB SSD"
        },
        "price": {
            "amount": 38990000,
            "currency": "VND"
        },
        "available": false
    },
    {
        "id": 3,
        "name": "Asus ROG Zephyrus G14",
        "specs": {
            "cpu": "AMD Ryzen 9",
            "ram": "32GB",
            "storage": "1TB SSD"
        },
        "price": {
            "amount": 42990000,
            "currency": "VND"
        },
        "available": true
    },
    {
        "id": 4,
        "name": "HP Spectre x360",
        "specs": {
            "cpu": "Intel i5",
            "ram": "8GB",
            "storage": "256GB SSD"
        },
        "price": {
            "amount": 23990000,
            "currency": "VND"
        },
        "available": true
    }
]


⭐️ Đọc data từ file JSON

@Test
public void testReadJsonFile() {
    //Get value from JSON object
    JsonHelpers.getValueJsonObject("platform");
    JsonHelpers.getValueJsonObject("device");

    JsonHelpers.getValueJsonObject("account", "username");
    JsonHelpers.getValueJsonObject("account", "password");

    JsonHelpers.getValueJsonObject("product", "product_name");
    JsonHelpers.getValueJsonObject("product", "product_price");
    JsonHelpers.getValueJsonObject("product", "hidden_product");
    JsonHelpers.getValueJsonObject("product", "product_image");

    JsonHelpers.getValueJsonObject_FilePath("src/test/resources/configs/device.json", "platforms", "android", "devices", "pixel8", "deviceName");

    //Get value from JSON array
    JsonHelpers.getValueJsonArray_FilePath("src/test/resources/test_data/sample_products.json", 0, "price", "amount");
}

 

🔆 Ghi data và cập nhật data vào JSON file

Chỗ này các hàm updateValueJson đã viết sẵn 2 trường hợp:

  • Cập nhật giá trị nếu key đã tồn tại (update)
  • Add key mới cùng giá trị mới nếu key chưa tồn tại (add)

@Test
public void testUpdateValueToJsonFile() {
    //Add new data with root key value
    JsonHelpers.updateValueJsonObject(
        "database_name", "Data 4");
    JsonHelpers.updateValueJsonObject(
        "database_index", 4);

    //Add new data with parent key is object
    JsonHelpers.updateValueJsonObject("database",
        "database_name", "Data 1");
    JsonHelpers.updateValueJsonObject("database",
        "database_index", 1);

    //Update value with parent key is object in file path
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "product", "product_name", "Product 3");
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "product", "product_price", 140000);

    //Add new data with parent key is object in file path
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "profile", "name", "Anh Tester");
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "profile", "email", "anhtester@example.com");
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "profile", "phone", "0939206009");
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "profile", "address", "HCM");
    JsonHelpers.updateValueJsonFile_Object("src/test/resources/test_data/data.json",
        "profile", "job_title", "Tester");

    //Update value in JSON array
    JsonHelpers.updateValueJsonArray_FilePath("src/test/resources/test_data/sample_products.json", "20000", 0, "price", "amount");
}


src/test/resources/test_data/data.json

{
   "platform": "Android",
   "device": "Pixel 8",
   "account": {
      "username": "admin",
      "password": "admin"
   },
   "table": {
      "table_name": "Table 1",
      "hidden_table": "false"
   },
   "product": {
      "product_name": "Product 3",
      "product_price": 140000,
      "hidden_product": "false",
      "product_image": "src/test/resources/test_data/product1.png"
   },
   "profile": {
      "name": "Anh Tester",
      "email": "anhtester@example.com",
      "phone": "0939206009",
      "address": "HCM",
      "job_title": "Tester"
   },
   "database_name": "Data 4",
   "database_index": 4,
   "database": {
      "database_name": "Data 1",
      "database_index": 1
   }
}


src/test/resources/test_data/sample_products.json

[
   {
      "id": 1,
      "name": "Laptop Dell XPS 13",
      "specs": {
         "cpu": "Intel i7",
         "ram": "16GB",
         "storage": "512GB SSD"
      },
      "price": {
         "amount": "20000",
         "currency": "VND"
      },
      "available": true
   },
   {
      "id": 2,
      "name": "MacBook Pro M2",
      "specs": {
         "cpu": "Apple M2",
         "ram": "16GB",
         "storage": "1TB SSD"
      },
      "price": {
         "amount": 38990000,
         "currency": "VND"
      },
      "available": false
   },
   {
      "id": 3,
      "name": "Asus ROG Zephyrus G14",
      "specs": {
         "cpu": "AMD Ryzen 9",
         "ram": "32GB",
         "storage": "1TB SSD"
      },
      "price": {
         "amount": 42990000,
         "currency": "VND"
      },
      "available": true
   },
   {
      "id": 4,
      "name": "HP Spectre x360",
      "specs": {
         "cpu": "Intel i5",
         "ram": "8GB",
         "storage": "256GB SSD"
      },
      "price": {
         "amount": 23990000,
         "currency": "VND"
      },
      "available": true
   }
]

 

✅ Đọc data từ JSON file để làm giá trị config cho device

Chúng ta sẽ đọc thông tin devices từ JSON file để tiện cho việc khai báo các thông tin một lần và gọi ra sử dụng lại thay vì truyền vào XML khá nhiều các fields thông tin.

Tạo file device.json trong mục src/test/resources/configs/device.json và khai báo theo cấu trúc sau:

{
   "platforms": {
      "android": {
         "devices": {
            "pixel8": {
               "platformName": "Android",
               "platformVersion": "15",
               "automationName": "UiAutomator2",
               "deviceName": "Pixel_8_Pro_API_35_2",
               "appPackage": "com.anhtester.mobile_app.taurus",
               "appActivity": "com.anhtester.mobile_app.taurus.MainActivity",
               "appAndroidPath": "src/test/resources/apps/flutter-app-release.apk",
               "noReset": false,
               "fullReset": false,
               "autoGrantPermissions": true
            },
            "pixel9": {
               "platformName": "Android",
               "platformVersion": "14",
               "automationName": "UiAutomator2",
               "deviceName": "Pixel_9_Pro_XL_API_34",
               "appPackage": "com.anhtester.mobile_app.taurus",
               "appActivity": "com.anhtester.mobile_app.taurus.MainActivity",
               "appAndroidPath": "src/test/resources/apps/flutter-app-release.apk",
               "noReset": false,
               "fullReset": false,
               "autoGrantPermissions": true
            }
         }
      },
      "ios": {
      }
   }
}

Sau này cần thêm fields gì thì cứ khai báo vào rồi đọc ra dùng. Tạm thời các thông tin chính như vậy.

Tiếp theo xây dựng các hàm xử lý chuyên dụng cho cấu trúc JSON mà mình đã thiết lập trong class ConfigData chung với các biến môi trường để gọi dùng chung cho tiện.

Hàm getValueJsonConfig(String platform, String device, String propertyName) chúng ta chỉ cần truyền 3 tham số platform, device propertyName để nó get ra đúng giá trị từng field cho từng loại thiết bị android hoặc ios đã thiết lập.

package com.anhtester.constants;

import com.anhtester.helpers.PropertiesHelpers;
import com.anhtester.helpers.SystemHelpers;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.IOException;

public class ConfigData {

    private ConfigData() {
        // Ngăn chặn khởi tạo class
    }

    // Load all properties files
    static {
        PropertiesHelpers.loadAllFiles();
    }

    public static final String PROJECT_PATH = SystemHelpers.getCurrentDir();
    public static final String EXCEL_DATA_FILE_PATH = PropertiesHelpers.getValue("EXCEL_DATA_FILE_PATH");
    public static final String JSON_DATA_FILE_PATH = PropertiesHelpers.getValue("JSON_DATA_FILE_PATH");
    public static final String JSON_CONFIG_FILE_PATH = PropertiesHelpers.getValue("JSON_CONFIG_FILE_PATH");
    public static final String TEST_DATA_FOLDER_PATH = PropertiesHelpers.getValue("TEST_DATA_FOLDER_PATH");
    public static final String LOCATE = PropertiesHelpers.getValue("LOCATE");
    public static final String TIMEOUT_SERVICE = PropertiesHelpers.getValue("TIMEOUT_SERVICE");
    public static final String TIMEOUT_EXPLICIT_DEFAULT = PropertiesHelpers.getValue("TIMEOUT_EXPLICIT_DEFAULT");
    public static final String APPIUM_DRIVER_LOCAL_SERVICE = PropertiesHelpers.getValue("APPIUM_DRIVER_LOCAL_SERVICE");

    public static final String STEP_ACTION_TIMEOUT = PropertiesHelpers.getValue("STEP_ACTION_TIMEOUT");

    public static String getValueJsonConfig(String platform, String device, String propertyName) {
        // Initialize Jackson ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        // Read JSON file
        JsonNode rootNode = null;
        try {
            rootNode = mapper.readTree(new File(ConfigData.JSON_CONFIG_FILE_PATH));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String result = rootNode
                .path("platforms")
                .path(platform.trim().toLowerCase())
                .path("devices")
                .path(device.trim().toLowerCase())
                .path(propertyName)
                .asText();

        System.out.println("***" + propertyName + ": " + result);
        return result;
    }

}

 

🔆 Truyền giá trị từ device.json vào BaseTest

Cập nhật class BaseTest lại như sau:

package com.anhtester.common;

import com.anhtester.constants.ConfigData;
import com.anhtester.drivers.DriverManager;
import com.anhtester.helpers.SystemHelpers;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
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.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;

import java.net.URL;
import java.time.Duration;
import java.util.Objects;

public class BaseTest_Json_Device {

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

    /**
     * Chạy Appium server với host và port được chỉ định.
     *
     * @param host Địa chỉ host của Appium server
     * @param port Port của Appium server
     */
    public void runAppiumServer(String host, String port) {
        System.out.println("HOST: " + host);
        System.out.println("PORT: " + port);

        //Set host and port
        if (host == null || host.isEmpty()) {
            host = HOST;
        } else {
            HOST = host;
        }

        if (port == null || port.isEmpty()) {
            port = PORT;
        } else {
            PORT = port;
        }

        TIMEOUT_SERVICE = Integer.parseInt(ConfigData.TIMEOUT_SERVICE);

        //Kill process on port
        SystemHelpers.killProcessOnPort(PORT);

        //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 on LOCAL.");
        }

    }

    /**
     * Thiết lập (khởi tạo và lưu trữ) AppiumDriver cho luồng hiện tại.
     *
     * @param platformName Tên platform (Android/iOS)
     * @param deviceName   Tên thiết bị trong device.json
     * @param udid         UDID của thiết bị Android (quan trọng cho parallel)
     * @param host         Địa chỉ host của Appium server
     * @param port         Port của Appium server
     * @param bundleId     Bundle ID của app iOS
     * @param wdaLocalPort Port WDA (iOS parallel)
     * @param systemPort   Port System (Android parallel)
     */
    @BeforeMethod(alwaysRun = true)
    @Parameters({"platformName", "deviceName", "udid", "host", "port", "bundleId", "wdaLocalPort", "systemPort"})
    public void setUpDriver(String platformName, String deviceName, @Optional String udid, String host, String port, @Optional String bundleId, @Optional String wdaLocalPort, @Optional String systemPort) {
        //Khởi động Appium server dưới máy local
        if (ConfigData.APPIUM_DRIVER_LOCAL_SERVICE.trim().equalsIgnoreCase("true")) {
            System.out.println("Khởi động Appium server LOCAL: " + host + ":" + port);
            runAppiumServer(host, port);
        } else {
            System.out.println("Chạy Appium server từ xa hoặc đã bật sẵn.");
        }

        //Print tất cả các thông số
        System.out.println("platformName: " + platformName);
        System.out.println("platformVersion: " + ConfigData.getValueJsonConfig(platformName, deviceName, "platformVersion"));
        System.out.println("deviceName: " + ConfigData.getValueJsonConfig(platformName, deviceName, "deviceName"));
        System.out.println("udid: " + ConfigData.getValueJsonConfig(platformName, deviceName, "udid"));
        System.out.println("automationName: " + ConfigData.getValueJsonConfig(platformName, deviceName, "automationName"));
        System.out.println("appPackage: " + ConfigData.getValueJsonConfig(platformName, deviceName, "appPackage"));
        System.out.println("appActivity: " + ConfigData.getValueJsonConfig(platformName, deviceName, "appActivity"));
        System.out.println("noReset: " + ConfigData.getValueJsonConfig(platformName, deviceName, "noReset"));
        System.out.println("fullReset: " + ConfigData.getValueJsonConfig(platformName, deviceName, "fullReset"));
        System.out.println("autoGrantPermissions: " + ConfigData.getValueJsonConfig(platformName, deviceName, "autoGrantPermissions"));
        System.out.println("host: " + host);
        System.out.println("port: " + port);
        System.out.println("bundleId: " + bundleId);
        System.out.println("wdaLocalPort: " + wdaLocalPort);
        System.out.println("systemPort: " + systemPort);

        AppiumDriver driver = null;

        try {
            if (platformName.equalsIgnoreCase("Android")) {
                UiAutomator2Options options = new UiAutomator2Options();
                options.setPlatformName(platformName);
                options.setPlatformVersion(ConfigData.getValueJsonConfig(platformName, deviceName, "platformVersion"));
                options.setDeviceName(ConfigData.getValueJsonConfig(platformName, deviceName, "deviceName"));

                if (udid != null && !udid.isEmpty()) {
                    options.setUdid(udid);
                }
                String appPackage = ConfigData.getValueJsonConfig(platformName, deviceName, "appPackage");
                if (appPackage != null && !appPackage.isEmpty()) {
                    options.setAppPackage(appPackage);
                }
                String appActivity = ConfigData.getValueJsonConfig(platformName, deviceName, "appActivity");
                if (appActivity != null && !appActivity.isEmpty()) {
                    options.setAppActivity(appActivity);
                }
                // options.setApp("/path/to/your/app.apk");
                options.setAutomationName(Objects.requireNonNullElse(ConfigData.getValueJsonConfig(platformName, deviceName, "automationName"), "UiAutomator2"));
                options.setNoReset(Boolean.parseBoolean(ConfigData.getValueJsonConfig(platformName, deviceName, "noReset")));
                options.setFullReset(Boolean.parseBoolean(ConfigData.getValueJsonConfig(platformName, deviceName, "fullReset")));

                if (systemPort != null && !systemPort.isEmpty()) {
                    options.setSystemPort(Integer.parseInt(systemPort));
                }

                driver = new AndroidDriver(new URL("http://" + host + ":" + port), options);
                System.out.println("Khởi tạo AndroidDriver cho thread: " + Thread.currentThread().getId() + " trên thiết bị: " + deviceName);


            } else if (platformName.equalsIgnoreCase("iOS")) {
                XCUITestOptions options = new XCUITestOptions();
                options.setPlatformName(platformName);
                options.setPlatformVersion(ConfigData.getValueJsonConfig(platformName, deviceName, "platformVersion"));
                options.setDeviceName(ConfigData.getValueJsonConfig(platformName, deviceName, "deviceName"));
                // options.setApp("/path/to/your/app.app or .ipa");
                if (bundleId != null && !bundleId.isEmpty()) {
                    options.setBundleId(bundleId);
                }

                options.setAutomationName(Objects.requireNonNullElse(ConfigData.getValueJsonConfig(platformName, deviceName, "automationName"), "XCUITest"));
                options.setNoReset(Boolean.parseBoolean(ConfigData.getValueJsonConfig(platformName, deviceName, "noReset")));
                options.setFullReset(Boolean.parseBoolean(ConfigData.getValueJsonConfig(platformName, deviceName, "fullReset")));

                if (wdaLocalPort != null && !wdaLocalPort.isEmpty()) {
                    options.setWdaLocalPort(Integer.parseInt(wdaLocalPort));
                }
                // options.setXcodeOrgId("YOUR_TEAM_ID");
                // options.setXcodeSigningId("iPhone Developer");

                driver = new IOSDriver(new URL("http://" + host + ":" + port), options);
                System.out.println("Khởi tạo IOSDriver cho thread: " + Thread.currentThread().getId() + " trên thiết bị: " + deviceName);

            } else {
                throw new IllegalArgumentException("Platform không hợp lệ: " + platformName);
            }

            // Lưu driver vào ThreadLocal
            DriverManager.setDriver(driver);

        } catch (Exception e) {
            System.err.println("❌Lỗi nghiêm trọng khi khởi tạo driver cho thread " + Thread.currentThread().getId() + " trên device " + deviceName + ": " + e.getMessage());
            // Có thể ném lại lỗi để TestNG biết test setup thất bại
            throw new RuntimeException("❌Không thể khởi tạo Appium driver ", e);
        }

    }

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

        //Dừng Appium server LOCAL nếu đã khởi động
        if (ConfigData.APPIUM_DRIVER_LOCAL_SERVICE.trim().equalsIgnoreCase("true")) {
            stopAppiumServer();
        }
    }

    /**
     * Stop Appium server.
     */
    public void stopAppiumServer() {
        if (service != null && service.isRunning()) {
            service.stop();
            System.out.println("##### Appium server stopped on " + HOST + ":" + PORT);
        }
        //Kill process on port
        SystemHelpers.killProcessOnPort(PORT);
    }

    /**
     * Tải xuống dữ liệu từ server. Chỉ dành cho Taurus App.
     *
     * @param dataNumber Số thứ tự của dữ liệu cần tải xuống
     */
    public void downloadDataFromServer(int dataNumber) {
        //Navigate to config to download database demo
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Config")).click();
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Server database")).click();
        MobileUI.sleep(2);
        DriverManager.getDriver().findElement(AppiumBy.xpath("//android.view.View[contains(@content-desc,'Data " + dataNumber + "')]/android.widget.Button")).click();
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Replace")).click();
        MobileUI.sleep(1);

        //Handle Alert Message, check displayed hoặc getText/getAttribute để kiểm tra nội dung message
        if (DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Downloaded")).isDisplayed()) {
            System.out.println("Database demo downloaded.");
        } else {
            System.out.println("Warning!! Can not download Database demo.");
        }
        MobileUI.sleep(2);
        DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Back")).click();
    }
}

 

🔆 Tạo suite XML và truyền lại giá trị

Bấy giờ các fields giá trị setup đã giảm đi khá nhiều so với buổi hôm trước.

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

<suite name="Suite Regression Test" verbose="1" parallel="tests">

    <test name="Platform Android Test 1 - Taurus App">
        <parameter name="platformName" value="Android"/>
        <parameter name="deviceName" value="pixel8"/>
        <parameter name="udid" value="emulator-5554"/>
        <parameter name="host" value="127.0.0.1"/>
        <parameter name="port" value="8000"/>
        <parameter name="systemPort" value="8201"/>
        <classes>
            <class name="com.anhtester.Bai22_JSON_File.testcases.LoginTest">
                <methods>
                    <include name="testLoginSuccess"/>
                </methods>
            </class>
        </classes>
    </test>

    <test name="Platform Android Test 2 - Taurus App">
        <parameter name="platformName" value="Android"/>
        <parameter name="deviceName" value="pixel9"/>
        <parameter name="udid" value="emulator-5556"/>
        <parameter name="host" value="127.0.0.1"/>
        <parameter name="port" value="9000"/>
        <parameter name="systemPort" value="8202"/>
        <classes>
            <class name="com.anhtester.Bai22_JSON_File.testcases.LoginTest">
                <methods>
                    <include name="testLoginFailWithUsernameInvalid"/>
                </methods>
            </class>
        </classes>
    </test>

    <!--    <test name="Platform iOS Test - Taurus App">-->
    <!--        <parameter name="platformName" value="iOS"/>-->
    <!--        <parameter name="deviceName" value="iphone14"/>-->
    <!--        <parameter name="bundleId" value=""/>-->
    <!--        <parameter name="host" value="127.0.0.1"/>-->
    <!--        <parameter name="port" value="6000"/>-->
    <!--        <parameter name="wdaLocalPort" value="8101"/>-->
    <!--        <classes>-->
    <!--            <class name="com.anhtester.Bai21_Config_File.testcases.MenuTest"/>-->
    <!--        </classes>-->
    <!--    </test>-->

</suite>

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