0 0
Read Time:11 Minute, 46 Second

Matchstick là một unit testing framework, được phát triển bởi LimeChain , cho phép các nhà phát triển subgraph kiểm tra mapping logic của họ trong môi trường sandboxed và tự tin triển khai subgraph của họ!

Giới thiệu 📌

Chúng ta đang cố gắng giải quyết điều gì ở đây?

Tất cả bắt đầu trở lại vào tháng 2 năm 2021 với  bài đăng trên diễn đàn này của Dennison từ  WithTally.com, nơi anh ấy đặt ra các vấn đề hoặc thiếu sót của nó, unit testing trong các subgraph. Và chính xác hơn – thử nghiệm các hàm mapping một cách riêng biệt.

Trước khi Matchstick xuất hiện, cách duy nhất để bạn có thể kiểm tra mapping logic của mình một cách chính xác là triển khai subgraph của bạn, đợi nó đồng bộ hóa, kiểm tra thủ công (gửi truy vấn) và sau đó nếu có điều gì đó không ổn – hãy chỉnh sửa code của bạn và lặp lại toàn bộ chu trình từ scratch. Như bạn có thể tưởng tượng, điều này là rất rườm rà để nói là tối thiểu. Đặc biệt nếu subgraph của bạn lớn, việc lập chỉ mục có thể mất vài ngày. Một trường hợp thậm chí còn điên rồ hơn là subgraph  Uniswap v2 , mất hơn một tháng  để đồng bộ hóa hoàn toàn.

Bạn có thể tưởng tượng rằng việc giới thiệu ngay cả một lỗi nhỏ nhất trong mapping cũng sẽ là một điều khó khăn khi bắt gặp một vòng lặp phản hồi kéo dài như vậy. Và điều này trở nên tồi tệ hơn theo cấp số nhân khi độ phức tạp của subgraph và số lượng nguồn dữ liệu tăng lên.

Vậy là đủ để tìm hiểu lý do cần phải có một công cụ như Matchstick, nếu bất kỳ ai quan tâm, họ luôn có thể xem bài đăng gốc và bình luận về nó.

Cách thức hoạt động 🧰

Chúng tôi đang cố gắng giải quyết nó như thế nào?

Kiểm soát dòng chảy

Để tạo ra một môi trường thử nghiệm thích hợp, nghĩa là càng gần  graph node càng tốt, chúng ta chỉ cần tạo proxy Matchstick thông qua graph–node, hoán đổi một số triển khai nhất định bằng các mô hình. Thay đổi quan trọng nhất cần lưu ý ở đây là Matchstick không duy trì trạng thái của kho lưu trữ subgraph trong Cơ sở dữ liệu PostgreSQL, mà là trong hashmap bộ nhớ được lập chỉ mục. Do đó, tất cả các phương thức lưu trữ (get, set, remove, v.v.) đều được tinh chỉnh và bây giờ trỏ đến hashmap, thay vì cơ sở dữ liệu. Tương tự đối với các cuộc gọi Ethereum RPC và rất nhiều thành phần bắt chược nhỏ hơn khác.

Tất nhiên, chúng tôi vẫn cần một cái gì đó để giúp giao diện subgraph với các triển khai mô phỏng thay vì thực. Phần còn thiếu đó là matchstick-as, được phân phối dưới dạng  module npm. Về cơ bản, nó  “dán”  Matchstick lên trên mapping code trong thời gian thực trong khi các thử nghiệm đang chạy.

TLDR –  Matchstick chỉ là một graph node được bắt chước lạ mắt  .

Cài đặt 🚀

Tại thời điểm viết bài này, Matchstick được phân phối dưới dạng một bản nhị phân độc lập, nhưng nó sẽ được tích hợp trong  graph-cli  trong tương lai gần.

Vì tập lệnh cài đặt khác nhau tùy thuộc vào OS của bạn và phiên bản Matchstick mới nhất hiện tại, vui lòng làm theo hướng dẫn cài đặt này .

Bạn cũng sẽ cần cài đặt matchstick-as trong dự án subgraph của mình, như sau: yarn add matchstick-as

Viết unit test đầu tiên của chúng tôi 👨‍💻

Hãy đi thẳng vào toàn bộ vấn đề của bài viết này – cách sử dụng Matchstick để viết các unit tests ngắn gọn và độc lập!

Giả sử bạn có các hàm mapping của mình  src/mapping.ts, bạn có thể tạo một  src/mapping.test.ts, nhưng tất nhiên, cách cấu trúc nó hoàn toàn phụ thuộc vào bạn.

Để giữ cho ví dụ này đơn giản nhất có thể, hãy giả sử chúng ta đang làm việc với một thực thể duy nhất trong lược đồ của chúng ta trông giống như sau:

type CustomEntity @entity {
id: ID!
name: String!
}

Và một sự kiện duy nhất:

NewCustomEntity(uint256, string)

Chúng tôi cũng có hàm xử lý minh họa sau (trong  src/mapping.ts)

1import { NewCustomEntity } from “../generated/MyDataSource/Example”;
2import { CustomEntity } from ‘../generated/schema’;
3
4export function handleNewCustomEntity(event: NewCustomEntity): void {
5let customEntity = new CustomEntity(event.params.id.toString());
6customEntity.name = event.params.name;
7
8customEntity.save();
9}
mapping.ts hosted with ❤ by GitHub

Bây giờ, chúng ta hãy bắt đầu viết các bài kiểm tra thực tế trong tệp src/mapping.test.ts. Trong ý chính được cung cấp bên dưới, bạn có thể xem một bài kiểm tra ví dụ trong đó chúng tôi xây dựng một phiên bản của sự kiện NewCustomEntity và đẩy một số tham số (id và name) vào nó. Sau đó, chúng tôi gọi handleNewCustomEntity() bằng sự kiện mà chúng tôi vừa tạo.

Sau đó, chúng tôi kiểm tra xem thực thể có được lưu đúng cách vào store hay không và nếu nó có các tham số và giá trị mà chúng tôi mong đợi. Chúng tôi đạt được điều đó với hàm assert.fieldEquals() được cung cấp bởi matchstick-as. Và cuối cùng, chúng tôi gọi clearStore() để bắt đầu thử nghiệm tiếp theo trên một clean slate .

1import { clearStore, test, assert, newMockEvent } from “matchstick-as/assembly/index”;
2import { ethereum } from “@graphprotocol/graph-ts”;
3import { NewCustomEntity } from “../generated/MyDataSource/Example”;
4import { handleNewCustomEntity } from “../mapping”;
5
6export function runTests(): void {
7test(“Example”, () => {
8// Initialise event (this can be generalised into a separate function)
9let newEntityEvent = newMockEvent(new NewCustomEntity()) as NewCustomEntity;
10newEntityEvent.parameters = [];
11let idParam = new ethereum.EventParam();
12idParam.value = ethereum.Value.fromI32(434);
13let nameParam = new ethereum.EventParam();
14nameParam.value = ethereum.Value.fromString(“Don Draper”);
15
16newEntityEvent.parameters.push(idParam);
17newEntityEvent.parameters.push(nameParam);
18
19// Call mappings
20handleNewCustomEntity(newEntityEvent);
21
22// Assert the state of the store
23assert.fieldEquals(“CustomEntity”, “434”, “name”, “Don Draper”);
24
25// Clear the store before the next test (optional)
26clearStore();
27});
28
29test(“Next test”, () => {
30//…
31});
32}
mapping.test.ts hosted with ❤ by GitHub

Một vài điều khác từ ví dụ trên đáng nói đến:
1. Các block thử nghiệm cần được bọc bằng hàm runTests().
2. Việc xác nhận trạng thái của cửa hàng hoạt động như thế này – chúng ta đang chuyển một sự kết hợp duy nhất của loại Thực thể và id. Sau đó, chúng ta kiểm tra một trường cụ thể trên thực thể đó và khẳng định rằng nó có giá trị mà chúng tôi mong đợi.

Xin chúc mừng, bạn vừa viết bài subgraph unit test đầu tiên của mình! 🎉 Nhưng đừng hào hứng – chúng ta vẫn chưa thể chạy thử nghiệm. Cuối cùng, chúng ta cần thêm nhập này vào tệp src/mapping.ts của mình:

export { runTests } from "../mapping.test.ts";

Chúng ta không thực sự sử dụng nó trong tệp đó, nhưng nó phải được nhập vào đó để nó có thể được Matchstick chọn khi chúng ta đang chạy thử nghiệm.

Bây giờ để chạy các bài kiểm tra của mình, chúng ta chỉ cần chạy như sau:

graph build && ./matchstick MyDataSource

Khi  MyDataSource là tên của nguồn dữ liệu mà bạn muốn kiểm tra (không quan trọng đó là nguồn dữ liệu mẫu hay nguồn thông thường). Sau khi chạy các bài kiểm tra, bạn sẽ thấy một cái gì đó như thế này trong thiết bị đầu cuối:

Các tình huống thử nghiệm phổ biến 🧪

Bây giờ chúng ta đã vượt qua rào cản đó, chúng ta có thể khám phá một số tình huống thử nghiệm phổ biến và tìm hiểu về các chức năng khác được cung cấp bởi Matchstick.

1. Thêm các thực thể vào cửa hàng mà không cần thông qua handler.

Đôi khi bạn không muốn thông qua handler sự kiện chỉ để tải một thực thể vào store. Trong trường hợp đó, bạn chỉ có thể làm:

1import { test, assert } from “matchstick-as/assembly/index”;
2import { CustomEntity } from ‘../../generated/schema’;
3import { Value } from “@graphprotocol/graph-ts”;
4
5export function runTests(): void {
6test(“Next test”, () => {
7let customEntity = new CustomEntity(“434”);
8customEntity.set(“name”, Value.fromString(“Frederick”));
9customEntity.save();
10
11assert.fieldEquals(“CustomEntity”, “434”, “name”, “Frederick”);
12});
13}
mapping.test.ts hosted with ❤ by GitHub

2. Bắt chước các cuộc gọi hợp đồng thông minh Ethereum

Khi bạn muốn mô phỏng một lệnh gọi hàm hợp đồng thông minh sẽ trả về như thế nào và kiểm tra cách mapping của bạn hoạt động như thế nào để đáp ứng với các giá trị trả về của hàm khác nhau, bạn có thể chỉ cần mô phỏng hàm trả về:

1import { assert, createMockedFunction } from “matchstick-as/assembly/index”;
2import { Contract } from “../../generated/MyDataSource/Example”;
3import { Address, ethereum } from “@graphprotocol/graph-ts”;
4
5let contractAddress = Address.fromString(“0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7”);
6let arg = ethereum.Value.fromString(“example string arg”);
7
8createMockedFunction(contractAddress, “functionName”, “functionName(string):(string)”)
9.withArgs([arg])
10.returns([ethereum.Value.fromString(“result”)]);
11
12let contract = Contract.bind(contractAddress);
13let result = contract.try_functionName(arg);
14
15assert.equals(ethereum.Value.fromString(“result”), ethereum.Value.fromString(result));
mapping.test.ts hosted with ❤ by GitHub

Bạn cũng có thể buộc hoàn nguyên hàm mô phỏng:

1import { assert, createMockedFunction } from “matchstick-as/assembly/index”;
2import { Contract } from “../../generated/MyDataSource/Example”;
3import { Address, ethereum } from “@graphprotocol/graph-ts”;
4
5let contractAddress = Address.fromString(“0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7”);
6let arg = ethereum.Value.fromString(“example string arg”);
7
8createMockedFunction(contractAddress, “functionName”, “functionName(string):(string)”)
9.withArgs([arg])
10.reverts();
mapping.test.ts hosted with ❤ by GitHub

3. Tương tác với Metadata Sự kiện

Đôi khi mapping logic của bạn có thể phụ thuộc vào metadata sự kiện. Sử dụng Matchstick, bạn có thể sử dụng metadata giao dịch mặc định, metadata này sẽ tồn tại trên bất kỳ đối tượng sự kiện mô phỏng nào, miễn là nó được tạo bằng hàm newMockEvent().

Ví dụ sau cho thấy cách bạn có thể đọc / ghi các trường đó trên đối tượng Sự kiện:

1import { newMockEvent } from “matchstick-as/assembly/index”;
2import { ethereum, Address } from “@graphprotocol/graph-ts”;
3import { NewCustomEntity } from “../generated/MyDataSource/Example”;
4
5let newEntityEvent = newMockEvent(new NewCustomEntity()) as NewCustomEntity;
6
7// Read
8let logType = newEntityEvent.logType;
9
10// Write
11let UPDATED_ADDRESS = “0xB16081F360e3847006dB660bae1c6d1b2e17eC2A”;
12newEntityEvent.address = Address.fromString(UPDATED_ADDRESS);
mapping.test.ts hosted with ❤ by GitHub

4. Khẳng định đẳng thức biến (variable equality)

Một trường hợp phổ biến khác là muốn khẳng định liệu biến X và biến Y có bằng nhau hay không. Bạn có thể làm điều đó với tất cả các loại Giá trị được hỗ trợ:

1import { assert } from “matchstick-as/assembly/index”;
2import { ethereum } from “@graphprotocol/graph-ts”;
3
4assert.equals(ethereum.Value.fromString(“hello”), ethereum.Value.fromString(“hello”));
mapping.test.ts hosted with ❤ by GitHub

Chẳng bao lâu nữa, sẽ có nhiều phương thức trợ giúp hơn được đưa vào lớp assert, ví dụ:  assert.addressEqual(a,b) thay vì assert.equals(ethereum.Value.fromAddress(a)),ethereum.Value.fromAddress(b));

5. Logging

Logging không khác quá nhiều so với graph node, đây là các ví dụ về việc sử dụng tất cả các loại log không quan trọng:

1import { test } from “matchstick-as/assembly/index”;
2import { log } from “matchstick-as/assembly/log”;
3
4export function runTests(): void {
5test(“Success”, () => {
6log.success(“Success!”);
7});
8test(“Error”, () => {
9log.error(“Error 🙁 “);
10});
11test(“Debug”, () => {
12log.debug(“Debugging…”);
13});
14test(“Info”, () => {
15log.info(“Info!”);
16});
17test(“Warning”, () => {
18log.warning(“Warning!”);
19});
20}
mapping.test.ts hosted with ❤ by GitHub

Chạy các ví dụ đó sẽ in ra thông tin sau:

Bạn cũng có thể mô phỏng một thất bại nghiêm trọng, như sau:

test("Blow everything up", () => {
log.critical("Boom!");
});

Điều này sẽ dẫn đến thông báo lỗi này:

6. Thời lượng kiểm tra

Sẽ rất nhiều thông tin để biết khi nào mapping logic của bạn cần một thời gian dài đặc biệt để thực hiện. Đó là lý do tại sao, như bạn có thể đã nhận thấy, chúng tôi in ra khoảng thời gian chạy thử nghiệm ở cuối đầu ra, ví dụ:

Jul 09 14:54:42.420 INFO Program execution time: 10.06022ms

Các bước tiếp theo 🎯

Khung  Matchstick  hiện đang hoạt động để thử nghiệm phiên bản beta. Có rất nhiều chỗ để cải thiện mọi thứ chúng ta đã nói ở trên. Chúng tôi đang cố gắng thu thập càng nhiều phản hồi từ các nhà phát triển subgraph càng tốt, để hiểu cách chúng ta có thể giải quyết các vấn đề mà họ gặp phải khi xây dựng subgraph, cũng như cách chúng ta có thể làm cho quá trình kiểm tra tổng thể suôn sẻ và hợp lý nhất có thể.

Có một bảng dự án GitHub nơi chúng tôi theo dõi công việc hàng ngày mà bạn có thể xem  tại đây .

Đọc thêm và hướng dẫn bằng video 📚

Nếu bạn thích phần giới thiệu nhanh này về Matchstick và muốn tìm hiểu thêm, vui lòng xem các  tài liệu chính thức  và xem các ví dụ đầy đủ về mọi thứ mà chúng tôi đã đề cập ở đây và hơn thế nữa trong subgraph demo của chúng tôi. Thưởng thức video dưới đây:

Phản hồi & Liên hệ📝

Nếu bạn có bất kỳ câu hỏi, phản hồi, yêu cầu tính năng nào hoặc chỉ muốn liên hệ, nơi tốt nhất sẽ là  The Graph Discord  , nơi chúng tôi có một kênh dành riêng cho Matchstick, được gọi là 🔥 | matchstick-early-testers (tên rất có thể sẽ thay đổi trong tương lai gần 😅). Bạn cũng có thể mở một issue trên issues page của chúng tôi  .

Như vậy là đủ cho cái này rồi, chúc bạn hack vui vẻ!

Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

Leave a Reply

Your email address will not be published. Required fields are marked *