Coder Social home page Coder Social logo

d_json_tool's Introduction

Compare two Json

1. Giới thiệu

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 json. Expected sẽ được query từ DB, actual 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ì

2. Cách giải quyết

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ì reflectionJson sẽ dễ đọc hơn khi thực hiện lưu kết quả của compare.

2.1. Giải thích cấu trúc output

updated

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

inserted

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.

deleted

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

2.2. Cách sử dụng

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.

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

Entity sau khi thực hiện action

object_after_add_contract_object
{
  "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();
diff output
{
  "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.

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 :

Before Object
{
  "employee":
  {
    "id": "1212",
    "updateTime" : 132245124312,
    "fullName":"John Miles",
    "age": 35
  },
  "dem" : 16,
  "createTime" : 12412412412
}
After Object :
After Object
{
  "employee": {
    "employId": "12122",
    "fullName": "John 1",
    "updateTime" : 13225124312,
    "age": 35,
    "contact": {
      "email": "[email protected]",
      "phone": "9999999"
    }
  },
   "createTime" : 1241312412
}

Code exclude 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 :

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

2.4. Insert/Delete Object In array

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 :

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

After Object :

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 :

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.

Mọi người sử dụng nếu có issue thì để lại cho tác giả fix nhé. Cảm ơn mọi người, nếu hay xin cho tác giả một star trên github nhé.

d_json_tool's People

Contributors

trandem avatar

Stargazers

Tran Nguyen Ban avatar Lý Duy avatar hanguyen87 avatar  avatar Thoth avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.