Developing > Cadre pour les tests unitaires

Cadre pour les tests unitaires

Reading time: 27 min

Matchstick est un cadre de test unitaire, développé par LimeChain, qui permet aux développeurs de subgraphs de tester leur logique de cartographie dans un environnement de type bac à sable et de déployer leurs subgraphs en toute confiance !

Démarrage

Lien vers cette section

Installer les dépendances

Lien vers cette section

Pour utiliser les méthodes d'assistance aux tests et exécuter les tests, vous devrez installer les dépendances suivantes :

yarn add --dev matchstick-as

graph-node dépend de PostgreSQL, donc si vous ne l'avez pas déjà, vous devrez l'installer. Nous vous conseillons vivement d'utiliser les commandes ci-dessous, car l'ajouter d'une autre manière peut provoquer des erreurs inattendues !

Commande d'installation Postgres :

brew install postgresql

Créez un lien symbolique vers la dernière libpq.5.lib Vous devrez peut-être d'abord créer ce répertoire /usr/local/opt/postgresql/lib/

ln -sf /usr/local/opt/postgresql@14/lib/postgresql@14/libpq.5.dylib /usr/local/opt/postgresql/lib/libpq.5.dylib

Commande d'installation de Postgres (dépend de votre distribution) :

sudo apt installer postgresql

WSL (Système Windows pour Linux)

Lien vers cette section

Vous pouvez utiliser Matchstick sur WSL en utilisant à la fois l'approche Docker et l'approche binaire. Comme WSL peut être un peu délicat, voici quelques conseils au cas où vous rencontreriez des problèmes tels que

static BYTES = Symbol("Bytes") SyntaxError: Unexpected token =

ou bien

<PROJECT_PATH>/node_modules/gluegun/build/index.js:13 throw up;

Veuillez vous assurer que vous utilisez une version plus récente de Node.js graph-cli qui ne prend plus en charge la v10.19.0, et qu'il s'agit toujours de la version par défaut pour le nouvel Ubuntu. images sur WSL. Par exemple, il est confirmé que Matchstick fonctionne sur WSL avec v18.1.0, vous pouvez y accéder soit via nvm ou si vous mettez à jour votre Node.js global. N'oubliez pas de supprimer node_modules et d'exécuter à nouveau npm install après avoir mis à jour votre nodejs ! Ensuite, assurez-vous que libpq est installé, vous pouvez le faire en exécutant

sudo apt-get install libpq-dev

Et en conclussion, n'utilisez pas graph test (qui utilise votre installation globale de graph-cli et pour une raison quelconque, cela semble être cassé sur WSL actuellement), utilisez plutôt yarn test ou npm run test (cela utilisera l'instance locale de graph-cli au niveau du projet, qui fonctionne à merveille). Pour cela, vous devez bien sûr avoir un script "test" dans votre fichier package.json qui peut être quelque chose d'aussi simple que

{
"name": "demo-subgraph",
"version": "0.1.0",
"scripts": {
"test": "graph test",
...
},
"dependencies": {
"@graphprotocol/graph-cli": "^0.56.0",
"@graphprotocol/graph-ts": "^0.31.0",
"matchstick-as": "^0.6.0"
}
}

Pour utiliser Matchstick dans votre projet de subgraph, il suffit d'ouvrir un terminal, de naviguer vers le dossier racine de votre projet et d'exécuter simplement graph test [options] <datasource> - il télécharge le dernier binaire Matchstick et exécute le test spécifié ou tous les tests dans un dossier de test (ou tous les tests existants si aucun datasource flag n'est spécifié).

Cette opération permet d'exécuter tous les tests contenus dans le dossier test :

graph test

Ceci lancera un test nommé gravity.test.ts et/ou tous les tests à l'intérieur d'un dossier nommé gravity :

gravity graph test

Ce fichier de test sera le seul à être exécuté :

graph test path/to/file.test.ts

Les Options :

-c, --coverage Exécuter les tests en mode couverture
-d, --docker Exécutez les tests dans un conteneur Docker (Remarque : veuillez exécuter à partir du dossier racine du subgraph)
-f, --force Binary : télécharge à nouveau le binaire. Docker : télécharge à nouveau le fichier Docker et reconstruit l'image Docker.
-h, --help Afficher les informations d'utilisation
-l, --logs Enregistre dans la console des informations sur le système d'exploitation, le modèle de processeur et l'URL de téléchargement (à des fins de débogage)
-r, --recompile Force les tests à être recompilés
-v, --version <tag> Choisissez la version du binaire Rust que vous souhaitez télécharger/utiliser

À partir de graph-cli 0.25.2, la commande graph test prend en charge l'exécution de matchstick dans un conteneur Docker avec le -d</0. > drapeau. L'implémentation de Docker utilise <a href="https://docs.docker.com/storage/bind-mounts/">bind mount</a> afin de ne pas avoir à reconstruire l'image Docker à chaque fois que la commande <code>graph test -d est exécutée. Vous pouvez également suivre les instructions du référentiel matchstick pour exécuter Docker manuellement.

graph test -d forces docker run to run with flag -t. This must be removed to run inside non-interactive environments (like GitHub CI).

❗ En cas d'exécution préalable de graph test, vous risquez de rencontrer l'erreur suivante lors de la construction de docker :

error from sender: failed to xattr node_modules/binary-install-raw/bin/binary-<platform>: permission denied

Dans ce cas, il faut créer un .dockerignore dans le dossier racine et ajoutez node_modules/binary-install-raw/bin

La Configuration

Lien vers cette section

Matchstick peut être configuré pour utiliser des tests personnalisés, des bibliothèques et un chemin de manifeste via le fichier de configuration matchstick.yaml :

testsFolder: path/to/tests
libsFolder: path/to/libs
manifestPath: path/to/subgraph.yaml

Subgraph démonstration

Lien vers cette section

Vous pouvez tester et jouer avec les exemples de ce guide en clonant le repo Demo Subgraph

Tutoriels vidéos

Lien vers cette section

Vous pouvez également consulter la série de vidéos sur « comment utiliser Matchstick pour écrire des tests unitaires pour vos subgraphs »

Tests structure

Lien vers cette section

IMPORTANT: The test structure described below depens on matchstick-as version >=0.5.0

describe(name: String , () => {}) - Définit un groupe de test.

Notez :

  • Les descriptions ne sont pas indispensable. Vous pouvez toujours utiliser test() à l'ancienne, en dehors des blocs describe()

L'exemple:

import { describe, test } from "matchstick-as/assembly/index"
import { handleNewGravatar } from "../../src/gravity"
describe("handleNewGravatar()", () => {
test("Devrait créer une nouvelle entité Gravatar", () => {
...
})
})

Exemple de décrire () imbriqué :

import { describe, test } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar } from "../../src/gravity"
describe("handleUpdatedGravatar()", () => {
describe("Lorsque l'entité existe", () => {
test("met à jour l'entité", () => {
...
})
})
describe("Lorsque l'entité n'existe pas", () => {
test("il crée une nouvelle entitéy", () => {
...
})
})
})

test(name: String, () =>, Should_fail: bool) - Définit un scénario de test. Vous pouvez utiliser test() à l’intérieur des blocs décrire() ou indépendamment.

L'exemple:

import { describe, test } from "matchstick-as/assembly/index"
import { handleNewGravatar } from "../../src/gravity"
describe("handleNewGravatar()", () => {
test("Devrait créer une nouvelle entité", () => {
...
})
})

ou bien

test("handleNewGravatar() doit créer une nouvelle entité", () => {
...
})

Exécute un bloc de code après tous les tests du fichier. Si afterAll est déclaré à l'intérieur d'un bloc describe, il est exécuté à la fin de ce bloc describe.

Les Exemples:

Le code contenu dans beforeAll sera exécuté une fois avant les tests all du fichier.

import { describe, test, beforeAll } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
import { Gravatar } from "../../generated/schema"
beforeAll(() => {
let gravatar = new Gravatar("0x0")
gravatar.displayName = “First Gravatar”
gravatar.save()
...
})
describe("Lorsque l'entité n'existe pas", () => {
test("il devrait créer un nouveau Gravatar avec l'id 0x1", () => {
...
})
})
describe("Lorsque l'entité existe déjà", () => {
test("il devrait mettre à jour le Gravatar avec l'id 0x0", () => {
...
})
})

Le code contenu dans beforeAll sera exécuté une fois avant tous les tests du premier bloc de description

import { describe, test, beforeAll } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
import { Gravatar } from "../../generated/schema"
describe("handleUpdatedGravatar()", () => {
beforeAll(() => {
let gravatar = new Gravatar("0x0")
gravatar.displayName = "Premier Gravatar"
gravatar.save()
...
})
test("met à jour le Gravatar avec l'identifiant 0x0", () => {
...
})
test("crée un nouveau Gravatar avec l'identifiant 0x1", () => ; {
...
})
})

Lance un bloc de code après tous les tests du fichier. Si afterAll est déclaré à l'intérieur d'un bloc describe, il s'exécute à la fin de ce bloc describe.

L'exemple:

Le code situé dans afterAll sera exécuté une fois après all tests dans le fichier.

import { describe, test, afterAll } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
import { store } from "@graphprotocol/graph-ts"
afterAll(() => {
store.remove("Gravatar", "0x0")
...
})
describe("handleNewGravatar, () => {
test("crée un Gravatar avec l'identifiant 0x0", () => {
...
})
})
describe("handleUpdatedGravatar", () => {
test("met à jour Gravatar avec l'identifiant 0x0", () => {
...
})
})

Le code à l'intérieur de afterAll s'exécute une fois après tous les tests du premier bloc de description

import { describe, test, afterAll, clearStore } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
describe("handleNewGravatar", () => {
afterAll(() => {
store.remove("Gravatar", "0x1")
...
})
test("Crée une nouvelle entité avec l'identifiant 0x0", () => {
...
})
test("Crée une nouvelle entité avec l'identifiant 0x1", () => {
...
})
})
describe("handleUpdatedGravatar", () => {
test("Met à jour le Gravatar avec l'identifiant 0x0", () => {
...
})
})

Lance un bloc de code avant chaque test. Si beforeEach est déclaré à l'intérieur d'un bloc describe, il s'exécute avant chaque test de ce bloc describe.

Exemples : Le code contenu dans beforeEach s'exécute avant chaque test.

importez { describe, test, beforeEach, clearStore } from "matchstick-as/assembly/index"
import { handleNewGravatars } from "./utils"
beforeEach(() => {
clearStore() // <-- clear the store before each test in the file
})
describe("handleNewGravatars, () => {
test("A test that requires a clean store", () => {
...
})
test("Second that requires a clean store", () => {
...
})
})
...

Exécutez un bloc de code avant chaque test. Si beforeEach est déclaré à l'intérieur d'un bloc describe, il s'exécute avant chaque test de ce bloc describe

import { describe, test, beforeEach } from 'matchstick-as/assembly/index'
import { handleUpdatedGravatar, handleNewGravatar } from '../../src/gravity'
describe('handleUpdatedGravatars', () => {
beforeEach(() => {
let gravatar = new Gravatar('0x0')
gravatar.displayName = 'First Gravatar'
gravatar.imageUrl = ''
gravatar.save()
})
test('Upates the displayName', () => {
assert.fieldEquals('Gravatar', '0x0', 'displayName', 'First Gravatar')
// code qui devrait mettre à jour le nom d'affichage à 1 Gravatar
assert.fieldEquals('Gravatar', '0x0', 'displayName', '1st Gravatar')
store.remove('Gravatar', '0x0')
})
test('Updates the imageUrl', () => {
assert.fieldEquals('Gravatar', '0x0', 'imageUrl', '')
// code qui devrait changer le imageUrl en https://www.gravatar.com/avatar/0x0
assert.fieldEquals('Gravatar', '0x0', 'imageUrl', 'https://www.gravatar.com/avatar/0x0')
store.remove('Gravatar', '0x0')
})
})

Lance un bloc de code après chaque test. Si afterEach est déclaré à l'intérieur d'un bloc de description, il s'exécute après chaque test de ce bloc de description.

Les Exemples:

Le code dans afterEach sera exécuté après chaque test.

import { describe, test, beforeEach, afterEach } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
beforeEach(() => {
let gravatar = new Gravatar("0x0")
gravatar.displayName = “First Gravatar”
gravatar.save()
})
afterEach(() => {
store.remove("Gravatar", "0x0")
})
describe("handleNewGravatar", () => {
...
})
describe("handleUpdatedGravatar", () => {
test("Upates the displayName", () => {
assert.fieldEquals("Gravatar", "0x0", "displayName", "First Gravatar")
// code qui devrait mettre à jour le nom d'affichage à 1 Gravatar
assert.fieldEquals("Gravatar", "0x0", "displayName", "1st Gravatar")
})
test("Updates the imageUrl", () => {
assert.fieldEquals("Gravatar", "0x0", "imageUrl", "")
// code qui devrait changer le imageUrl en https://www.gravatar.com/avatar/0x0
assert.fieldEquals("Gravatar", "0x0", "imageUrl", "https://www.gravatar.com/avatar/0x0")
})
})

Le code contenu dans afterEach exécutera après chaque test dans cette description

import { describe, test, beforeEach, afterEach } from "matchstick-as/assembly/index"
import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"
describe("handleNewGravatar", () => {
...
})
describe("handleUpdatedGravatar", () => {
beforeEach(() => {
let gravatar = new Gravatar("0x0")
gravatar.displayName = "First Gravatar"
gravatar.imageUrl = ""
gravatar.save()
})
afterEach(() => {
store.remove("Gravatar", "0x0")
})
test("Upates the displayName", () => {
assert.fieldEquals("Gravatar", "0x0", "displayName", "First Gravatar")
// code qui devrait mettre à jour le nom d'affichage à 1 Gravatar
assert.fieldEquals("Gravatar", "0x0", "displayName", "1st Gravatar")
})
test("Updates the imageUrl", () => {
assert.fieldEquals("Gravatar", "0x0", "imageUrl", "")
// code qui devrait changer le imageUrl en https://www.gravatar.com/avatar/0x0
assert.fieldEquals("Gravatar", "0x0", "imageUrl", "https://www.gravatar.com/avatar/0x0")
})
})
fieldEquals(entityType: string, id: string, fieldName: string, expectedVal: string)
equals(expected: ethereum.Value, actual: ethereum.Value)
notInStore(entityType: string, id: string)
addressEquals(address1: Address, address2: Address)
bytesEquals(bytes1: Bytes, bytes2: Bytes)
i32Equals(number1: i32, number2: i32)
bigIntEquals(bigInt1: BigInt, bigInt2: BigInt)
booleanEquals(bool1: boolean, bool2: boolean)
stringEquals(string1: string, string2: string)
arrayEquals(array1: Array<ethereum.Value>, array2: Array<ethereum.Value>)
tupleEquals(tuple1: ethereum.Tuple, tuple2: ethereum.Tuple)
assertTrue(value: boolean)
assertNull<T>(value: T)
assertNotNull<T>(value: T)
entityCount(entityType: string, expectedCount: i32)

As of version 0.6.0, asserts support custom error messages as well

assert.fieldEquals('Gravatar', '0x123', 'id', '0x123', 'Id should be 0x123')
assert.equals(ethereum.Value.fromI32(1), ethereum.Value.fromI32(1), 'Value should equal 1')
assert.notInStore('Gravatar', '0x124', 'Gravatar should not be in store')
assert.addressEquals(Address.zero(), Address.zero(), 'Address should be zero')
assert.bytesEquals(Bytes.fromUTF8('0x123'), Bytes.fromUTF8('0x123'), 'Bytes should be equal')
assert.i32Equals(2, 2, 'I32 should equal 2')
assert.bigIntEquals(BigInt.fromI32(1), BigInt.fromI32(1), 'BigInt should equal 1')
assert.booleanEquals(true, true, 'Boolean should be true')
assert.stringEquals('1', '1', 'String should equal 1')
assert.arrayEquals([ethereum.Value.fromI32(1)], [ethereum.Value.fromI32(1)], 'Arrays should be equal')
assert.tupleEquals(
changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),
changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),
'Tuples should be equal',
)
assert.assertTrue(true, 'Should be true')
assert.assertNull(null, 'Should be null')
assert.assertNotNull('not null', 'Should be not null')
assert.entityCount('Gravatar', 1, 'There should be 2 gravatars')
assert.dataSourceCount('GraphTokenLockWallet', 1, 'GraphTokenLockWallet template should have one data source')
assert.dataSourceExists(
'GraphTokenLockWallet',
Address.zero().toHexString(),
'GraphTokenLockWallet should have a data source for zero address',
)

Écrire un test unitaire

Lien vers cette section

Voyons à quoi ressemblerait un test unitaire simple en utilisant les exemples Gravatar dans le subgraph de démonstration.

En supposant que nous disposions de la fonction de traitement suivante (ainsi que de deux fonctions d'aide pour nous faciliter la vie) :

export function handleNewGravatar(event: NewGravatar): void {
let gravatar = new Gravatar(event.params.id.toHex())
gravatar.owner = event.params.owner
gravatar.displayName = event.params.displayName
gravatar.imageUrl = event.params.imageUrl
gravatar.save()
}
export function handleNewGravatars(events: NewGravatar[]): void {
events.forEach((event) => {
handleNewGravatar(event)
})
}
export function createNewGravatarEvent(
id: i32,
ownerAddress: string,
displayName: string,
imageUrl: string,
): NewGravatar {
let mockEvent = newMockEvent()
let newGravatarEvent = new NewGravatar(
mockEvent.address,
mockEvent.logIndex,
mockEvent.transactionLogIndex,
mockEvent.logType,
mockEvent.block,
mockEvent.transaction,
mockEvent.parameters,
)
newGravatarEvent.parameters = new Array()
let idParam = new ethereum.EventParam('id', ethereum.Value.fromI32(id))
let addressParam = new ethereum.EventParam(
'ownderAddress',
ethereum.Value.fromAddress(Address.fromString(ownerAddress)),
)
let displayNameParam = new ethereum.EventParam('displayName', ethereum.Value.fromString(displayName))
let imageUrlParam = new ethereum.EventParam('imageUrl', ethereum.Value.fromString(imageUrl))
newGravatarEvent.parameters.push(idParam)
newGravatarEvent.parameters.push(addressParam)
newGravatarEvent.parameters.push(displayNameParam)
newGravatarEvent.parameters.push(imageUrlParam)
return newGravatarEvent
}

Nous devons tout d'abord créer un fichier de test dans notre projet. Voici un exemple de ce à quoi cela pourrait ressembler :

import { clearStore, test, assert } from 'matchstick-as/assembly/index'
import { Gravatar } from '../../generated/schema'
import { NewGravatar } from '../../generated/Gravity/Gravity'
import { createNewGravatarEvent, handleNewGravatars } from '../mappings/gravity'
test('Peut appeler des mappings avec des événements personnalisés', () => {
// Crée une entité de test et l'enregistre dans le store comme état initial (optionnel)
let gravatar = new Gravatar('gravatarId0')
gravatar.save()
// Crée des événements factices
let newGravatarEvent = createNewGravatarEvent(12345, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')
let anotherGravatarEvent = createNewGravatarEvent(3546, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')
// Appelle les fonctions de mapping en passant les événements qu'on vient de créer
handleNewGravatars([newGravatarEvent, anotherGravatarEvent])
// Vérifie l'état du store
assert.fieldEquals('Gravatar', 'gravatarId0', 'id', 'gravatarId0')
assert.fieldEquals('Gravatar', '12345', 'owner', '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7')
assert.fieldEquals('Gravatar', '3546', 'displayName', 'cap')
// Vide le store afin de commencer le prochain test avec un état propre
clearStore()
})
test('Test suivant', () => {
//...
})

Cela fait beaucoup à décortiquer ! Tout d'abord, une chose importante à noter est que nous importons des choses de matchstick-as, notre bibliothèque d'aide AssemblyScript (distribuée en tant que module npm). Vous pouvez trouver le dépôt ici. matchstick-as nous fournit des méthodes de test utiles et définit également la fonction test() que nous utiliserons pour construire nos blocs de test. Le reste est assez simple - voici ce qui se passe :

  • Mettons en place notre état initial et ajoutons une entité Gravatar personnalisée ;
  • Définissons deux objets événement NewGravatar avec leurs données, en utilisant la fonction createNewGravatarEvent() ;
  • Appelons des méthodes de gestion pour ces événements - handleNewGravatars() et nous passons la liste de nos événements personnalisés ;
  • Affirmons l'état du magasin. Comment cela fonctionne-t-il ? - Nous passons une combinaison unique de type d'entité et d'identifiant. Ensuite, nous vérifions un champ spécifique de cette entité et affirmons qu'il a la valeur que nous attendons. Nous faisons cela à la fois pour l'entité Gravatar initiale que nous avons ajoutée au magasin, ainsi que pour les deux entités Gravatar qui sont ajoutées lorsque la fonction de gestion est appelée ;
  • Et enfin, Nettoyons le magasin à l'aide de clearStore() afin que notre prochain test puisse commencer avec un objet magasin frais et vide. Nous pouvons définir autant de blocs de test que nous le souhaitons.

Et voilà, nous avons formulé notre premier test ! 👏

Maintenant, afin d'exécuter nos tests, il suffit d'exécuter ce qui suit dans le dossier racine de votre subgraph :

gravity graph test

Et si tout se passe bien, vous devriez être accueilli par ce qui suit :

Matchstick indiquant « Tous les tests sont réussis ! »

Scénarios de tests actuels

Lien vers cette section

L'Hydratation du magasin avec un certain état

Lien vers cette section

Les utilisateurs peuvent hydrater le magasin avec un ensemble connu d'entités. Voici un exemple pour initialiser la boutique avec une entité Gravatar :

laissez gravatar = new Gravatar('entryId')
gravatar.save()

Appel d'une fonction de cartographie avec un événement

Lien vers cette section

Un utilisateur peut créer un événement personnalisé et le transmettre à une fonction de cartographie liée au magasin :

import { store } from 'matchstick-as/assembly/store'
import { NewGravatar } from '../../generated/Gravity/Gravity'
import { handleNewGravatars, createNewGravatarEvent } from './mapping'
let newGravatarEvent = createNewGravatarEvent(12345, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')
handleNewGravatar(newGravatarEvent)

Appel de tous les mappages avec des projecteurs d'événements

Lien vers cette section

Les utilisateurs peuvent appeler les mappages avec des dispositifs de test.

import { NewGravatar } from '../../generated/Gravity/Gravity'
import { store } from 'matchstick-as/assembly/store'
import { handleNewGravatars, createNewGravatarEvent } from './mapping'
let newGravatarEvent = createNewGravatarEvent(12345, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')
let anotherGravatarEvent = createNewGravatarEvent(3546, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')
handleNewGravatars([newGravatarEvent, anotherGravatarEvent])
export function handleNewGravatars(events: NewGravatar[]): void {
events.forEach(event => {
handleNewGravatar(event);
});
}

Appels de contrat moqueurs

Lien vers cette section

Les utilisateurs peuvent simuler des appels de contrat :

import { addMetadata, assert, createMockedFunction, clearStore, test } from 'matchstick-as/assembly/index'
import { Gravity } from '../../generated/Gravity/Gravity'
import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'
let contractAddress = Address.fromString('0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7')
let expectedResult = Address.fromString('0x90cBa2Bbb19ecc291A12066Fd8329D65FA1f1947')
let bigIntParam = BigInt.fromString('1234')
createMockedFunction(contractAddress, 'gravatarToOwner', 'gravatarToOwner(uint256):(address)')
.withArgs([ethereum.Value.fromSignedBigInt(bigIntParam)])
.returns([ethereum.Value.fromAddress(Address.fromString('0x90cBa2Bbb19ecc291A12066Fd8329D65FA1f1947'))])
let gravity = Gravity.bind(contractAddress)
let result = gravity.gravatarToOwner(bigIntParam)
assert.equals(ethereum.Value.fromAddress(expectedResult), ethereum.Value.fromAddress(result))

Comme démontré, afin de se moquer d'un appel de contrat et d'obtenir une valeur de retour, l'utilisateur doit fournir une adresse de contrat, un nom de fonction, une signature de fonction, un tableau d'arguments et bien sûr – la valeur de retour.

Utilisateurs peuvent également simuler des annulations de fonctions :

laissez contractAddress = Address.fromString('0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7')
createMockedFunction(contractAddress, 'getGravatar', 'getGravatar(address):(string,string)')
.withArgs([ethereum.Value.fromAddress(contractAddress)])
.reverts()

Se moquer des fichiers IPFS (à partir de Matchstick 0.4.1)

Lien vers cette section

Les utilisateurs peuvent simuler les fichiers IPFS en utilisant la fonction mockIpfsFile(hash, filePath). La fonction accepte deux arguments, le premier est le hachage/chemin du fichier IPFS et le second est le chemin d'accès à un fichier local.

NOTEZ : Lorsque vous testez ipfs.map/ipfs.mapJSON, la fonction de rappel doit être exportée du fichier de test afin que matchstck puisse la détecter, comme la fonction processGravatar() dans l'exemple de test ci-dessous :

Fichier .test.ts :

import { assert, test, mockIpfsFile } from 'matchstick-as/assembly/index'
import { ipfs } from '@graphprotocol/graph-ts'
import { gravatarFromIpfs } from './utils'
// Exportation du callback ipfs.map() pour que matchstck le détecte.
export { processGravatar } from './utils'
test('ipfs.cat', () => {
mockIpfsFile('ipfsCatfileHash', 'tests/ipfs/cat.json')
assert.entityCount(GRAVATAR_ENTITY_TYPE, 0)
gravatarFromIpfs()
assert.entityCount(GRAVATAR_ENTITY_TYPE, 1)
assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '1', 'imageUrl', 'https://i.ytimg.com/vi/MELP46s8Cic/maxresdefault.jpg')
clearStore()
})
test('ipfs.map', () => {
mockIpfsFile('ipfsMapfileHash', 'tests/ipfs/map.json')
assert.entityCount(GRAVATAR_ENTITY_TYPE, 0)
ipfs.map('ipfsMapfileHash', 'processGravatar', Value.fromString('Gravatar'), ['json'])
assert.entityCount(GRAVATAR_ENTITY_TYPE, 3)
assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '1', 'displayName', 'Gravatar1')
assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '2', 'displayName', 'Gravatar2')
assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '3', 'displayName', 'Gravatar3')
})

Fichier utils.ts :

import { Address, ethereum, JSONValue, Value, ipfs, json, Bytes } from "@graphprotocol/graph-ts"
import { Gravatar } from "../../generated/schema"
...
// rappel ipfs.map
export function processGravatar(value: JSONValue, userData: Value): void {
// Consultez la documentation de JSONValue pour plus de détails sur la façon de traiter les données.
// avec JSON values
let obj = value.toObject()
let id = obj.get('id')
if (!id) {
return
}
// Des entités de rappel peuvent également être créées
let gravatar = new Gravatar(id.toString())
gravatar.displayName = userData.toString() + id.toString()
gravatar.save()
}
// fonction qui appelle ipfs.cat
export function gravatarFromIpfs(): void {
let rawData = ipfs.cat("ipfsCatfileHash")
if (!rawData) {
return
}
let jsonData = json.fromBytes(rawData as Bytes).toObject()
let id = jsonData.get('id')
let url = jsonData.get("imageUrl")
if (!id || !url) {
return
}
let gravatar = new Gravatar(id.toString())
gravatar.imageUrl = url.toString()
gravatar.save()
}

Affirmation de l'état du magasin

Lien vers cette section

Les utilisateurs sont en mesure d'affirmer l'état final (ou intermédiaire) du magasin via des entités d'affirmation. Pour ce faire, l'utilisateur doit fournir un type d'entité, l'ID spécifique d'une entité, le nom d'un champ sur cette entité et la valeur attendue du champ. Voici un exemple rapide:

import { assert } from 'matchstick-as/assembly/index'
import { Gravatar } from '../generated/schema'
let gravatar = new Gravatar('gravatarId0')
gravatar.save()
assert.fieldEquals('Gravatar', 'gravatarId0', 'id', 'gravatarId0')

L'exécution de la fonction assert.fieldEquals() vérifiera l'égalité du champ donné par rapport à la valeur attendue indiquée. Le test échouera et un message d'erreur sera généré si les valeurs sont NON égales. Sinon, le test réussira.

Interagir avec les métadonnées d'événement

Lien vers cette section

Les utilisateurs peuvent utiliser les métadonnées de transaction par défaut, qui peuvent être renvoyées comme un ethereum. Event en utilisant la fonction newMockEvent(). L'exemple suivant montre comment vous pouvez lire/écrire dans ces champs sur l'objet Event :

// Lisez
let logType = newGravatarEvent.logType
// Écrivez
let UPDATED_ADDRESS = '0xB16081F360e3847006dB660bae1c6d1b2e17eC2A'
newGravatarEvent.address = Address.fromString(UPDATED_ADDRESS)

Affirmation de l'égalité des variables

Lien vers cette section
assert.equals(ethereum.Value.fromString("bonjour"); ethereum.Value.fromString("bonjour"));

Affirmez qu'une entité n'existe pas dans le magasin

Lien vers cette section

Les utilisateurs peuvent affirmer qu'une entité n'existe pas dans le magasin. La fonction prend un type d'entité et un identifiant. Si l'entité se trouve effectivement dans le magasin, le test échouera avec un message d'erreur pertinent. Voici un exemple rapide de la façon d'utiliser cette fonctionnalité :

assert.notInStore('Gravatar', '23')

Printing the whole store, or single entities from it (for debug purposes)

Lien vers cette section

Vous pouvez imprimer l'intégralité du magasin sur la console à l'aide de cette fonction d'assistance:

import { logStore } from 'matchstick-as/assembly/store'
logStore()

As of version 0.6.0, logStore no longer prints derived fields, instead users can use the new logEntity function. Of course logEntity can be used to print any entity, not just ones that have derived fields. logEntity takes the entity type, entity id and a showRelated flag to indicate if users want to print the related derived entities.

import { logEntity } from 'matchstick-as/assembly/store'
logEntity("Gravatar", 23, true)

Échec prévu

Lien vers cette section

Les utilisateurs peuvent s'attendre à des échecs de test, en utilisant l'indicateur ShouldFail sur les fonctions test() :

test(
'Devrait générer une erreur',
() => {
lancer une nouvelle erreur()
},
vrai,
)

Si le test est marqué avec ShouldFail = true mais n'échoue PAS, cela apparaîtra comme une erreur dans les journaux et le bloc de test échouera. De plus, s'il est marqué avec ShouldFail = false (l'état par défaut), l'exécuteur de test plantera.

Journal de bord

Lien vers cette section

Avoir des journaux personnalisés dans les tests unitaires équivaut exactement à la journalisation des mappages. La différence est que l'objet journal doit être importé depuis matchstick-as plutôt que graph-ts. Voici un exemple simple avec tous les types de journaux non critiques :

import { test } from "matchstick-as/assembly/index";
import { log } from "matchstick-as/assembly/log";
test("Success", () => {
log.success("Success!". []);
});
test("Error", () => {
log.error("Error :( ", []);
});
test("Debug", () => {
log.debug("Debugging...", []);
});
test("Info", () => {
log.info("Info!", []);
});
test("Warning", () => {
log.warning("Warning!", []);
});

Les utilisateurs peuvent également simuler une panne critique, comme ceci :

test('Tout faire exploser', () => {
log.critical('Boom!')
})

La journalisation des erreurs critiques arrêtera l’exécution des tests et fera tout exploser. Après tout, nous voulons nous assurer que votre code ne contient pas de journaux critiques lors du déploiement, et vous devriez le remarquer immédiatement si cela devait se produire.

Tests dérivés

Lien vers cette section

Testing derived fields is a feature which allows users to set a field on a certain entity and have another entity be updated automatically if it derives one of its fields from the first entity.

Before version 0.6.0 it was possible to get the derived entities by accessing them as entity fields/properties, like so:

let entity = ExampleEntity.load('id')
let derivedEntity = entity.derived_entity

As of version 0.6.0, this is done by using the loadRelated function of graph-node, the derived entities can be accessed the same way as in the handlers.

test('Derived fields example test', () => {
let mainAccount = GraphAccount.load('12')!
assert.assertNull(mainAccount.get('nameSignalTransactions'))
assert.assertNull(mainAccount.get('operatorOf'))
let operatedAccount = GraphAccount.load('1')!
operatedAccount.operators = [mainAccount.id]
operatedAccount.save()
mockNameSignalTransaction('1234', mainAccount.id)
mockNameSignalTransaction('2', mainAccount.id)
mainAccount = GraphAccount.load('12')!
assert.assertNull(mainAccount.get('nameSignalTransactions'))
assert.assertNull(mainAccount.get('operatorOf'))
const nameSignalTransactions = mainAccount.nameSignalTransactions.load()
const operatorsOfMainAccount = mainAccount.operatorOf.load()
assert.i32Equals(2, nameSignalTransactions.length)
assert.i32Equals(1, operatorsOfMainAccount.length)
assert.stringEquals('1', operatorsOfMainAccount[0].id)
mockNameSignalTransaction('2345', mainAccount.id)
let nst = NameSignalTransaction.load('1234')!
nst.signer = '11'
nst.save()
store.remove('NameSignalTransaction', '2')
mainAccount = GraphAccount.load('12')!
assert.i32Equals(1, mainAccount.nameSignalTransactions.load().length)
})

Testing loadInBlock

Lien vers cette section

As of version 0.6.0, users can test loadInBlock by using the mockInBlockStore, it allows mocking entities in the block cache.

import { afterAll, beforeAll, describe, mockInBlockStore, test } from 'matchstick-as'
import { Gravatar } from '../../generated/schema'
describe('loadInBlock', () => {
beforeAll(() => {
mockInBlockStore('Gravatar', 'gravatarId0', gravatar)
})
afterAll(() => {
clearInBlockStore()
})
test('Can use entity.loadInBlock() to retrieve entity from cache store in the current block', () => {
let retrievedGravatar = Gravatar.loadInBlock('gravatarId0')
assert.stringEquals('gravatarId0', retrievedGravatar!.get('id')!.toString())
})
test("Returns null when calling entity.loadInBlock() if an entity doesn't exist in the current block", () => {
let retrievedGravatar = Gravatar.loadInBlock('IDoNotExist')
assert.assertNull(retrievedGravatar)
})
})

Tester les sources de données dynamiques

Lien vers cette section

Le test des sources de données dynamiques peut être effectué en simulant la valeur de retour des fonctions context(), address() et network() du Espace de noms dataSource. Ces fonctions renvoient actuellement les éléments suivants : context() - renvoie une entité vide (DataSourceContext), address() - renvoie 0x000000000000000000000000000000000000000000, network() - renvoie mainnet. Les fonctions create(...) et createWithContext(...) sont simulées pour ne rien faire, elles n'ont donc pas du tout besoin d'être appelées dans les tests. Les modifications des valeurs de retour peuvent être effectuées via les fonctions de l'espace de noms dataSourceMock dans matchstick-as (version 0.3.0+).

L'exemple ci-dessous :

Nous avons d’abord le gestionnaire d’événements suivant (qui a été intentionnellement réutilisé pour présenter la moquerie de la source de données) :

fonction d'exportation handleApproveTokenDestinations (événement : ApproveTokenDestinations) : void {
laissez tokenLockWallet = TokenLockWallet.load(dataSource.address().toHexString()) !
if (dataSource.network() == 'rinkeby') {
tokenLockWallet.tokenDestinationsApproved = true
}
laissez contexte = dataSource.context()
if (context.get('contextVal')!.toI32() > 0) {
tokenLockWallet.setBigInt('tokensReleased', BigInt.fromI32(context.get('contextVal')!.toI32()))
}
tokenLockWallet.save()
}

Et puis nous avons le test utilisant l'une des méthodes de l'espace de noms dataSourceMock pour définir une nouvelle valeur de retour pour toutes les fonctions dataSource :

importer { assert, test, newMockEvent, dataSourceMock } depuis 'matchstick-as/assembly/index'
importer { BigInt, DataSourceContext, Value } depuis '@graphprotocol/graph-ts'
importer { handleApproveTokenDestinations } depuis '../../src/token-lock-wallet'
importer { ApproveTokenDestinations } depuis '../../generated/templates/GraphTokenLockWallet/GraphTokenLockWallet'
importer { TokenLockWallet } depuis '../../generated/schema'
test('Exemple moqueur simple de source de données', () => {
laissez adresseString = '0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'
let adresse = Adresse.fromString(addressString)
laissez wallet = new TokenLockWallet (address.toHexString())
portefeuille.save()
laisser contexte = new DataSourceContext()
contexte.set('contextVal', Value.fromI32(325))
dataSourceMock.setReturnValues(addressString, 'rinkeby', contexte)
let event = changetype<ApproveTokenDestinations>(newMockEvent())
assert.assertTrue(!wallet.tokenDestinationsApproved)
handleApproveTokenDestinations (événement)
portefeuille = TokenLockWallet.load(address.toHexString()) !
assert.assertTrue(wallet.tokenDestinationsApproved)
assert.bigIntEquals(wallet.tokensReleased, BigInt.fromI32(325))
dataSourceMock.resetValues()
})

Notez que dataSourceMock.resetValues() est appelé à la fin. C'est parce que les valeurs sont mémorisées lorsqu'elles sont modifiées et doivent être réinitialisées si vous voulez revenir aux valeurs par défaut.

Testing dynamic data source creation

Lien vers cette section

As of version 0.6.0, it is possible to test if a new data source has been created from a template. This feature supports both ethereum/contract and file/ipfs templates. There are four functions for this:

  • assert.dataSourceCount(templateName, expectedCount) can be used to assert the expected count of data sources from the specified template
  • assert.dataSourceExists(templateName, address/ipfsHash) asserts that a data source with the specified identifier (could be a contract address or IPFS file hash) from a specified template was created
  • logDataSources(templateName) prints all data sources from the specified template to the console for debugging purposes
  • readFile(path) reads a JSON file that represents an IPFS file and returns the content as Bytes

Testing ethereum/contract templates

Lien vers cette section
test('ethereum/contract dataSource creation example', () => {
// Assert there are no dataSources created from GraphTokenLockWallet template
assert.dataSourceCount('GraphTokenLockWallet', 0)
// Create a new GraphTokenLockWallet datasource with address 0xA16081F360e3847006dB660bae1c6d1b2e17eC2A
GraphTokenLockWallet.create(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'))
// Assert the dataSource has been created
assert.dataSourceCount('GraphTokenLockWallet', 1)
// Add a second dataSource with context
let context = new DataSourceContext()
context.set('contextVal', Value.fromI32(325))
GraphTokenLockWallet.createWithContext(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'), context)
// Assert there are now 2 dataSources
assert.dataSourceCount('GraphTokenLockWallet', 2)
// Assert that a dataSource with address "0xA16081F360e3847006dB660bae1c6d1b2e17eC2B" was created
// Keep in mind that `Address` type is transformed to lower case when decoded, so you have to pass the address as all lower case when asserting if it exists
assert.dataSourceExists('GraphTokenLockWallet', '0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'.toLowerCase())
logDataSources('GraphTokenLockWallet')
})
Example logDataSource output
Lien vers cette section
🛠 {
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a": {
"kind": "ethereum/contract",
"name": "GraphTokenLockWallet",
"address": "0xa16081f360e3847006db660bae1c6d1b2e17ec2a",
"context": null
},
"0xa16081f360e3847006db660bae1c6d1b2e17ec2b": {
"kind": "ethereum/contract",
"name": "GraphTokenLockWallet",
"address": "0xa16081f360e3847006db660bae1c6d1b2e17ec2b",
"context": {
"contextVal": {
"type": "Int",
"data": 325
}
}
}
}

Testing file/ipfs templates

Lien vers cette section

Similarly to contract dynamic data sources, users can test test file datas sources and their handlers

Example subgraph.yaml
Lien vers cette section
...
templates:
- kind: file/ipfs
name: GraphTokenLockMetadata
network: mainnet
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
file: ./src/token-lock-wallet.ts
handler: handleMetadata
entities:
- TokenLockMetadata
abis:
- name: GraphTokenLockWallet
file: ./abis/GraphTokenLockWallet.json
Example schema.graphql
Lien vers cette section
"""
Token Lock Wallets which hold locked GRT
"""
type TokenLockMetadata @entity {
"The address of the token lock wallet"
id: ID!
"Start time of the release schedule"
startTime: BigInt!
"End time of the release schedule"
endTime: BigInt!
"Number of periods between start time and end time"
periods: BigInt!
"Time when the releases start"
releaseStartTime: BigInt!
}
Example metadata.json
Lien vers cette section
{
"startTime": 1,
"endTime": 1,
"periods": 1,
"releaseStartTime": 1
}
Example handler
Lien vers cette section
export function handleMetadata(content: Bytes): void {
// dataSource.stringParams() returns the File DataSource CID
// stringParam() will be mocked in the handler test
// for more info https://thegraph.com/docs/en/developing/creating-a-subgraph/#create-a-new-handler-to-process-files
let tokenMetadata = new TokenLockMetadata(dataSource.stringParam())
const value = json.fromBytes(content).toObject()
if (value) {
const startTime = value.get('startTime')
const endTime = value.get('endTime')
const periods = value.get('periods')
const releaseStartTime = value.get('releaseStartTime')
if (startTime && endTime && periods && releaseStartTime) {
tokenMetadata.startTime = startTime.toBigInt()
tokenMetadata.endTime = endTime.toBigInt()
tokenMetadata.periods = periods.toBigInt()
tokenMetadata.releaseStartTime = releaseStartTime.toBigInt()
}
tokenMetadata.save()
}
}
import { assert, test, dataSourceMock, readFile } from 'matchstick-as'
import { Address, BigInt, Bytes, DataSourceContext, ipfs, json, store, Value } from '@graphprotocol/graph-ts'
import { handleMetadata } from '../../src/token-lock-wallet'
import { TokenLockMetadata } from '../../generated/schema'
import { GraphTokenLockMetadata } from '../../generated/templates'
test('file/ipfs dataSource creation example', () => {
// Generate the dataSource CID from the ipfsHash + ipfs path file
// For example QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm/example.json
const ipfshash = 'QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm'
const CID = `${ipfshash}/example.json`
// Create a new dataSource using the generated CID
GraphTokenLockMetadata.create(CID)
// Assert the dataSource has been created
assert.dataSourceCount('GraphTokenLockMetadata', 1)
assert.dataSourceExists('GraphTokenLockMetadata', CID)
logDataSources('GraphTokenLockMetadata')
// Now we have to mock the dataSource metadata and specifically dataSource.stringParam()
// dataSource.stringParams actually uses the value of dataSource.address(), so we will mock the address using dataSourceMock from matchstick-as
// First we will reset the values and then use dataSourceMock.setAddress() to set the CID
dataSourceMock.resetValues()
dataSourceMock.setAddress(CID)
// Now we need to generate the Bytes to pass to the dataSource handler
// For this case we introduced a new function readFile, that reads a local json and returns the content as Bytes
const content = readFile(`path/to/metadata.json`)
handleMetadata(content)
// Now we will test if a TokenLockMetadata was created
const metadata = TokenLockMetadata.load(CID)
assert.bigIntEquals(metadata!.endTime, BigInt.fromI32(1))
assert.bigIntEquals(metadata!.periods, BigInt.fromI32(1))
assert.bigIntEquals(metadata!.releaseStartTime, BigInt.fromI32(1))
assert.bigIntEquals(metadata!.startTime, BigInt.fromI32(1))
})

Couverture de test

Lien vers cette section

Grâce à Matchstick, les développeurs de subgraphs peuvent exécuter un script qui calculera la couverture des tests unitaires écrits.

L'outil de couverture de test prend les binaires de test wasm compilés et les convertit en fichiers wat, qui peuvent ensuite être facilement inspectés pour voir si les gestionnaires définis dans subgraph .yaml ont été appelés. Étant donné que la couverture du code (et les tests dans leur ensemble) en sont à leurs tout premiers stades dans AssemblyScript et WebAssembly, Matchstick ne peut pas vérifier la couverture des branches. Au lieu de cela, nous nous appuyons sur l'affirmation selon laquelle si un gestionnaire donné a été appelé, l'événement/la fonction correspondant a été correctement simulé.

Conditions préalables

Lien vers cette section

Pour exécuter la fonctionnalité de couverture de test fournie dans Matchstick, vous devez préparer quelques éléments au préalable :

Exportez vos gestionnaires

Lien vers cette section

Pour que Matchstick vérifie quels gestionnaires sont exécutés, ces gestionnaires doivent être exportés à partir du fichier de test. Ainsi, par exemple, dans notre exemple, dans notre fichier gravitation.test.ts, nous avons le gestionnaire suivant en cours d'importation :

importez { handleNewGravatar } from '../../src/gravity'

Pour que cette fonction soit visible (pour qu'elle soit incluse dans le fichier wat par son nom), nous devons également l'exporter, comme ceci :

exportez { handleNewGravatar }

Une fois tout configuré, pour exécuter l'outil de couverture de test, exécutez simplement :

graph test -- -c

Vous pouvez également ajouter une commande coverage personnalisée à votre fichier package.json, comme ceci :

"scripts": {
/.../
"coverage": "test graph -- -c"
},

Cela exécutera l'outil de couverture et vous devriez voir quelque chose comme ceci dans le terminal :

$ graph test -c
Sauter l'étape de téléchargement/installation car le binaire existe déjà à l'adresse suivante : /Users/petko/work/demo-subgraph/node_modules/binary-install-raw/bin/0.4.0
___ ___ _ _ _ _ _
| \/ | | | | | | | (_) | |
| . . | __ _| |_ ___| |__ ___| |_ _ ___| | __
| |\/| |/ _` | __/ __| '_ \/ __| __| |/ __| |/ /
| | | | (_| | || (__| | | \__ \ |_| | (__| <
\_| |_/\__,_|\__\___|_| |_|___/\__|_|\___|_|\_\
Compilation...
Exécution en mode rapport de couverture.
Lecture des modules de test générés... 🔎️
Génération du rapport de couverture 📝
Handlers for source 'Gravity':
Handler 'handleNewGravatar' is tested.
Handler 'handleUpdatedGravatar' is not tested.
Handler 'handleCreateGravatar' is tested.
Test coverage: 66.7% (2/3 handlers).
Handlers for source 'GraphTokenLockWallet':
Handler 'handleTokensReleased' is not tested.
Handler 'handleTokensWithdrawn' is not tested.
Handler 'handleTokensRevoked' is not tested.
Handler 'handleManagerUpdated' is not tested.
Handler 'handleApproveTokenDestinations' is not tested.
Handler 'handleRevokeTokenDestinations' is not tested.
Test coverage: 0.0% (0/6 handlers).
Global test coverage: 22.2% (2/9 handlers).

Durée d'exécution du test dans la sortie du journal

Lien vers cette section

La sortie du journal inclut la durée de l’exécution du test. Voici un exemple :

[Jeudi 31 mars 2022 13:54:54 +0300] Programme exécuté en : 42,270 ms.

Erreurs de compilation courantes

Lien vers cette section

Critique : impossible de créer WasmInstance à partir d'un module valide avec un contexte : importation inconnue : wasi_snapshot_preview1::fd_write n'a pas été défini

Cela signifie que vous avez utilisé console.log dans votre code, ce qui n'est pas pris en charge par AssemblyScript. Veuillez envisager d'utiliser l'API Logging

ERREUR TS2554 : attendu ? arguments, mais j'ai eu ?.

renvoyer le nouveau ethereum.Block (defaultAddressBytes, defaultAddressBytes, defaultAddressBytes, defaultAddress, defaultAddressBytes, defaultAddressBytes, defaultAddressBytes, defaultBigInt, defaultBigInt, defaultBigInt, defaultBigInt, defaultBigInt, defaultBigInt, defaultBigInt, defaultBigInt) ;

dans ~lib/matchstick-as/assembly/defaults.ts(18,12)

ERROR TS2554: Expected ? arguments, but got ?.

renvoyer un nouveau ethereum.Transaction (defaultAddressBytes, defaultBigInt, defaultAddress, defaultAddress, defaultBigInt, defaultBigInt, defaultBigInt, defaultAddressBytes, defaultBigInt) ;

dans ~lib/matchstick-as/assembly/defaults.ts(24,12)

L'inadéquation des arguments est causée par une inadéquation entre graph-ts et matchstick-as. La meilleure façon de résoudre des problèmes comme celui-ci est de tout mettre à jour vers la dernière version publiée.

Si vous avez des questions, des commentaires, des demandes de fonctionnalités ou si vous souhaitez simplement nous contacter, le meilleur endroit serait The Graph Discord où nous avons une chaîne dédiée à Matchstick, appelée 🔥| tests unitaires.

Modifier une page

Précédente
Common AssemblyScript Issues
Suivante
FAQs pour les développeurs
Modifier une page