Trích xuất thông tin khác nhau từ hai Entity

Bài viết được sự cho phép của tác giả Trần Văn Dem

Trong quá trình phát triển các sản phẩm phần mềm, việc kiểm thử là bước vô cùng quan trọng. Không có kiểm thử thì phần mềm phát triển không đủ sự tin tưởng.

Hiện nay với sự phức tạp của các nghiệp vụ do đó các Entity trong các sản phẩm phần mềm ngày càng trở lên phức tạp :

  • Có rất nhiều trường (field) > 20 fields
  • Lưu trữ vật lý tại nhiều bảng khác nhau hoặc nhiều loại DB khác nhau
  • Cấu trúc của một Entity thường sẽ phức tạp: chứa nhiều object khác, array object,…

Khi viết iteration test thông thường chúng ta sẽ chỉ thực hiện assert cho một vài trường nghĩ là sẽ thay đổi sau khi gọi qua API điều này là đúng nhưng chưa đủ.

Ví dụ khi ta lập trình gọi API cho phép thay đổi giá của sản phẩm nhưng không may trong quá trình đó lại thực hiện cập nhật nhầm một thuộc tính nào đó như số lượng tồn kho. Việc này là rất nguy hiểm thường sảy ra khi code của project đã quá lớn và là người mới join vào quá trình phát triển chưa nắm rõ. Khi thực hiện testing nếu chỉ thực hiện việc assert cho trường giá thì vẫn chưa đủ.

Một cách dễ dàng có thể khắc phục là thực hiện compare toàn bộ 2 entity dưới dạng jsonExpected sẽ được query từ DBactual sẽ được lưu trong 1 file text. Nếu 2 object này giống nhau hoàn toàn thì có thể chắc chắn được API chúng ta đang hoạt động đúng.

Thư viện sử dụng có thể là:

        <dependency>
            <groupId>org.skyscreamer</groupId>
            <artifactId>jsonassert</artifactId>
            <version>1.5.0</version>
        </dependency>

Hoặc dùng chính project để thực hiện compare này.

Cách này khá dễ thực hiện nhưng sẽ có một số nhược điểm :

  • Khi ta thêm, xóa trường trong Entity thì tất cả các file json sẽ bị ảnh hưởng và bắt buộc phải thay đổi theo.
  • Khi đọc test thì người đọc sẽ rất khó biết được API thực sự thay đổi điều gì

Project này đề xuất phương pháp compare 2 json object, trích xuất sự khác biệt và lưu nó dưới dạng một cấu trúc json để dễ đọc hiểu.

Cấu trúc json được đề xuất như sau:

{
  "updated": {
  },
  "inserted" : {
  },
  "deleted": {
  }
}

Project sẽ sử dụng thuật toán DFS để duyệt qua và thực hiện compare 2 Json. Project sử dụng Json thay vì reflection vì Json sẽ dễ đọc hơn khi thực hiện lưu kết quả của compare.

  Kiểm thử ứng dụng Web – Ứng dụng Web là gì?
  Thực thi phương thức kiểm thử NUnit với Command Line

Chứa các field mang giá tri nguyên thủy, có thay đổi giá trị trước và sau updated.

Chứa các field mang giá trị là object hoặc array được thêm mới vào object

Các field mới xuất hiện tại object sau khi thực hiện action, trước đó object không tồn tại field này.

Chứa các field mang giá trị là object hoặc array được xóa đi trong object sau khi update

Chi tiết về cách sử dụng tham khảo các test được viết sẵn :

Ví dụ ta có object sau :

Entity trước khi thực hiện action.

{
  "employee":
  {
    "id": "1212",
    "fullName":"John Miles",
    "age": 35
  },
  "dem" : 16
}

Entity sau khi thực hiện action

{
  "employee": {
    "employId": "12122",
    "fullName": "John 1",
    "age": 35,
    "contact": {
      "email": "[email protected]",
      "phone": "9999999"
    }
  }
}

Sau khi thực hiện compare sẽ được kết quả như sau :

 var diffJson = new DDiffJsonBuilder()
                .insertBuilder( new InsertObjectBuilder())
                .updateBuilder(new UpdateObjectBuilder())
                .deleteBuilder(new DeleteFlattenKeyBuilder())
                .build();

diffJson.diffScan(beforeObject, afterObject);

var output = diffJson.toJsonFormatString();
{
  "updated": {
    "employee": {
      "fullName": "John 1"
    }
  },
  "inserted": {
    "employee": {
      "employId": "12122",
      "contact": {
        "email": "[email protected]",
        "phone": "9999999"
      }
    }
  },
  "deleted": {
    "employee.id": 1,
    "dem": 1
  }
}

Với Object nhỏ thì việc thực hiện chạy qua DiffJson thì không nhận thấy được ưu điểm nhưng nếu là một Object lớn và phức tạp, khi thực hiện API chỉ thay đổi vài trường thì sẽ thấy được lợi ích.

Tìm ngay việc làm Tester Hồ Chí Minh HOT tại TopDev!

2.3. Exclude fields

Khi thực hiện API có một số trường chúng ta không muốn compare như updated timestamp, trường timestamp này có thể có ở tất cả object nên việc loại trừ được các trường này cũng rất cần thiết.

Before Object :

{
  "employee":
  {
    "id": "1212",
    "updateTime" : 132245124312,
    "fullName":"John Miles",
    "age": 35
  },
  "dem" : 16,
  "createTime" : 12412412412
}

After Object :

{
  "employee": {
    "employId": "12122",
    "fullName": "John 1",
    "updateTime" : 13225124312,
    "age": 35,
    "contact": {
      "email": "[email protected]",
      "phone": "9999999"
    }
  },
   "createTime" : 1241312412
}

Code excludes timestamp

JsonNode beforeObject = DJacksonCommon.loadJsonFromFile("before_timestamp.json");
        JsonNode afterObject = DJacksonCommon.loadJsonFromFile("after_timestamp.json");
        var diffCompare = new DDiffJsonBuilder()
                .insertBuilder( new InsertObjectBuilder())
                .updateBuilder(new UpdateObjectBuilder())
                .excludeCompareFieldPath("createTime")
                .excludeCompareFieldPath("employee.updateTime")
                .deleteBuilder(new DeleteFlattenKeyBuilder())
                .build();

        diffCompare.diffScan(beforeObject, afterObject);

        String output = diffCompare.toJsonFormatString();
        System.out.println(output);

Output Object :

{
  "updated": {
    "employee": {
      "fullName": "John 1"
    }
  },
  "deleted": {
    "employee.id": 1,
    "dem": 1
  },
  "inserted": {
    "employee": {
      "employId": "12122",
      "contact": {
        "email": "[email protected]",
        "phone": "9999999"
      }
    }
  }
}

Một tính năng nữa của DDiff là có thể xác định được object nào đã bị xóa, thêm vào, update trong json array

Before Object :

{
  "plants": [
    {
      "plantId": "1",
      "name": "plant1"
    },
    {
      "plantId": "2",
      "name": "plant2"
    },
    {
      "plantId": "3",
      "name": "plant3"
    }
  ],
  "demtv": 11
}

After Object :

{
  "plants": [
    {
      "plantId": "1",
      "name": "plant11"
    },
    {
      "plantId": "3",
      "name": "plant3"
    },
    {
      "plantId": "4",
      "name": "plant4"
    }
  ],
  "demtv": 11
}

Code for object have array:

        JsonNode beforeObject = DJacksonCommon.loadJsonFromFile("array_json_sample/before_have_array_plant.json");
        JsonNode afterObject = DJacksonCommon.loadJsonFromFile("array_json_sample/after_have_array_plant.json");

        diffJson.registerObjectKeyInArrayByPath("plants","plantId");
        diffJson.diffScan(beforeObject, afterObject);

        String output = diffJson.toJsonFormatString();

Output Object :

{
  "updated": {
    "plants": [
      {
        "plantId": "1",
        "name": "plant11"
      }
    ]
  },
  "inserted": {
    "plants": [
      {
        "plantId": "4",
        "name": "plant4"
      }
    ]
  },
  "deleted": {
    "plants.plantId.2": 1
  }
}

3. Tổng kết

Các cách dùng kỹ hơn về thư viện này vui lòng tham khảo tại thư mục test. Test của Project đạt đến 83% line vậy nên có thể tin tưởng để sử dụng.

Bài viết gốc được đăng tải tại demtv.hashnode.dev

Xem thêm:

Hàng loạt việc làm IT hấp dẫn trên TopDev đang chờ bạn ứng tuyển.