subgraphs > Developing > Creating > Cadre pour les tests unitaires

Cadre pour les tests unitaires

Reading time: 28 min

Learn how to use Matchstick, a unit testing framework developed by LimeChain. Matchstick enables subgraph developers to test their mapping logic in a sandboxed environment and sucessfully deploy their subgraphs.

Benefits of Using Matchstick

Lien vers cette section
  • It's written in Rust and optimized for high performance.
  • It gives you access to developer features, including the ability to mock contract calls, make assertions about the store state, monitor subgraph failures, check test performance, and many more.

Install Dependencies

Lien vers cette section

In order to use the test helper methods and run tests, you need to install the following dependencies:

yarn add --dev matchstick-as

Install PostgreSQL

Lien vers cette section

graph-node depends on PostgreSQL, so if you don't already have it, then you will need to install it.

Note: It's highly recommended to use the commands below to avoid unexpected errors.

Installation command:

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

Installation command (depends on your distro):

sudo apt installer postgresql

Using WSL (Windows Subsystem for 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"
}
}

Using Matchstick

Lien vers cette section

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 force docker run à s'exécuter avec le paramètre -t. Cela doit être supprimé pour s'exécuter dans des environnements non interactifs (comme 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 »

Structure des tests

Lien vers cette section

IMPORTANT: La structure de test décrite ci-dessous dépend de la version 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)

À partir de la version 0.6.0, les assertions supportent également les messages d'erreur personnalisés

assert.fieldEquals('Gravatar', '0x123', 'id', '0x123', 'L'Id doit être 0x123')
assert.equals(ethereum.Value.fromI32(1), ethereum.Value.fromI32(1), 'La valeur doit être égale à 1')
assert.notInStore('Gravatar', '0x124', 'Gravatar ne doit pas être dans le magasin')
assert.addressEquals(Address.zero(), Address.zero(), 'L'adresse doit être zéro')
assert.bytesEquals(Bytes.fromUTF8('0x123'), Bytes.fromUTF8('0x123'), 'Les Bytes doivent être égaux')
assert.i32Equals(2, 2, 'I32 doit être égal à 2')
assert.bigIntEquals(BigInt.fromI32(1), BigInt.fromI32(1), 'BigInt doit être égal à 1')
assert.booleanEquals(true, true, 'Le booléen doit être vrai')
assert.stringEquals('1', '1', 'La chaîne de caractère doit être égale à 1')
assert.arrayEquals([ethereum.Value.fromI32(1)], [ethereum.Value.fromI32(1)], 'Les tableaux doivent être égaux')
assert.tupleEquals(
changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),
changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),
'Les tuples doivent être égaux',
)
assert.assertTrue(true, 'Doit être vrai')
assert.assertNull(null, 'Doit être nul')
assert.assertNotNull('pas nul', 'Ne doit pas être nul')
assert.entityCount('Gravatar', 1, 'Il devrait y avoir 2 gravatars')
assert.dataSourceCount('GraphTokenLockWallet', 1, 'Le modèle(template) GraphTokenLockWallet doit avoir une source de données')
assert.dataSourceExists(
'GraphTokenLockWallet',
Address.zero().toHexString(),
'GraphTokenLockWallet doit avoir une source de données pour l'adresse zéro',
)

É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')

Affichage de tout le magasin ou d'entités individuelles (à des fins de débogage)

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()

À partir de la version 0.6.0, logStore n'affiche plus les champs dérivés, les utilisateurs peuvent utiliser la nouvelle fonction logEntity. Bien sûr, logEntity peut être utilisé pour afficher n'importe quelle entité, pas seulement celles qui ont des champs dérivés. logEntity prend le type d'entité, l'Id de l'entité et un paramètre showRelated pour indiquer si les utilisateurs veulent afficher les entités dérivées liées.

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

Tester les champs dérivés est une fonctionnalité qui permet aux utilisateurs de définir un champ sur une certaine entité et de faire en sorte qu'une autre entité soit automatiquement mise à jour si elle dérive l'un de ses champs de la première entité.

Avant la version 0.6.0, il était possible d'obtenir les entités dérivées en les accédant comme des champs/propriétés d'entité, comme ceci :

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

À partir de la version 0.6.0, cela se fait en utilisant la fonction loadRelated de graph-node, les entités dérivées peuvent être accessibles de la même manière que dans les gestionnaires.

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)
})

Test de loadInBlock

Lien vers cette section

Depuis la version 0.6.0, les utilisateurs peuvent tester loadInBlock en utilisant mockInBlockStore, ce qui permet de simuler des entités dans le cache du bloc.

import { afterAll, beforeAll, describe, mockInBlockStore, test } from 'matchstick-as'
import { Gravatar } from '../../generated/schema'
describe('loadInBlock', () => {
beforeAll(() => {
mockInBlockStore('Gravatar', 'gravatarId0', gravatar)
})
afterAll(() => {
clearInBlockStore()
})
test('On peut utiliser entity.loadInBlock() pour récupérer l'entité dans le cache du bloc actuel', () => {
let retrievedGravatar = Gravatar.loadInBlock('gravatarId0')
assert.stringEquals('gravatarId0', retrievedGravatar!.get('id')!.toString())
})
test("Renvoie null lors de l'appel de entity.loadInBlock() si une entité n'existe pas dans le bloc actuel", () => {
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.

Test de la création dynamique de sources de données

Lien vers cette section

Depuis la version 0.6.0, il est possible de tester si une nouvelle source de données a été créée à partir d'un modèle. Cette fonctionnalité prend en charge les modèles ethereum/contrat et file/ipfs. Il existe quatre fonctions pour cela :

  • assert.dataSourceCount(templateName, expectedCount) peut être utilisée pour affirmer le nombre attendu de sources de données à partir du modèle spécifié
  • assert.dataSourceExists(templateName, address/ipfsHash) affirme qu'une source de données avec l'identifiant spécifié (qui peut être une adresse de contrat ou un hash de fichier IPFS) a été créée à partir d'un modèle spécifié
  • logDataSources(templateName) affiche toutes les sources de données à partir du modèle spécifié dans la console à des fins de débogage
  • readFile(path) lit un fichier JSON qui représente un fichier IPFS et retourne le contenu sous forme de Bytes

Test des modèles ethereum/contract

Lien vers cette section
test('ethereum/contract dataSource creation example', () => {
// affirme qu'aucune source de données n'est créée à partir du modèle GraphTokenLockWallet
assert.dataSourceCount('GraphTokenLockWallet', 0)
// Crée une nouvelle source de données GraphTokenLockWallet avec l'adresse 0xA16081F360e3847006dB660bae1c6d1b2e17eC2A
GraphTokenLockWallet.create(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'))
// affirme que la source de données a été créée
assert.dataSourceCount('GraphTokenLockWallet', 1)
// Ajoute une seconde source de données avec contexte
let context = new DataSourceContext()
context.set('contextVal', Value.fromI32(325))
GraphTokenLockWallet.createWithContext(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'), context)
// Vérifie qu'il y a maintenant 2 sources de données
assert.dataSourceCount('GraphTokenLockWallet', 2)
// affirme qu'une source de données avec l'adresse "0xA16081F360e3847006dB660bae1c6d1b2e17eC2B" a été créée
// Gardez à l'esprit que le type `Address` est transformé en minuscules lors du décodage, vous devez donc passer l'adresse en minuscules lorsque vous affirmez son existence
assert.dataSourceExists('GraphTokenLockWallet', '0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'.toLowerCase())
logDataSources('GraphTokenLockWallet')
})
Exemple de sortie de logDataSource
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
}
}
}

Test des modèles file/ipfs

Lien vers cette section

De même que les sources de données dynamiques de contrat, les utilisateurs peuvent tester les fichiers sources de données test et leurs gestionnaires

Exemple 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
Exemple de fichier schema.graphql
Lien vers cette section
"""
Portefeuilles de verrouillage de jetons qui contiennent des GRT verrouillés
"""
type TokenLockMetadata @entity {
"L'adresse du portefeuille de blocage des jetons"
id: ID!
"Heure de début du calendrier de sortie"
startTime: BigInt!
"Heure de fin du calendrier de sortie""
endTime: BigInt!
"Nombre de périodes entre l'heure de début et l'heure de fin"
periods: BigInt!
"Heure à laquelle commence la sortie"
releaseStartTime: BigInt!
}
Exemple de fichier metadata.json
Lien vers cette section
{
"startTime": 1,
"endTime": 1,
"periods": 1,
"releaseStartTime": 1
}
Exemple de gestionnaire
Lien vers cette section
export function handleMetadata(content: Bytes): void {
// dataSource.stringParams() renvoie le CID du fichier de source de donnée
// stringParam() sera simulé dans le test du gestionnaire
// pour plus d'informations 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()
}
}
Exemple de test
Lien vers cette section
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('exemple de création de source de données file/ipfs', () => {
// Générer le CID de la source de données à partir du ipfsHash + chemin du fichier du ipfs
// Par exemple QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm/example.json
const ipfshash = 'QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm'
const CID = `${ipfshash}/example.json`
// Création d'une nouvelle source de données en utilisant le CID généré
GraphTokenLockMetadata.create(CID)
// Affirmer que la source de données a été créée
assert.dataSourceCount('GraphTokenLockMetadata', 1)
assert.dataSourceExists('GraphTokenLockMetadata', CID)
logDataSources('GraphTokenLockMetadata')
// Maintenant, nous devons simuler les métadonnées de la source de données et plus particulièrement dataSource.stringParam()
// dataSource.stringParams utilise en fait la valeur de dataSource.address(), donc nous allons simuler l'adresse en utilisant dataSourceMock de matchstick-as
// Tout d'abord, nous allons réinitialiser les valeurs et ensuite utiliser dataSourceMock.setAddress() pour définir le CID
dataSourceMock.resetValues()
dataSourceMock.setAddress(CID)
// Maintenant, nous devons générer les Bytes à passer au gestionnaire de la source de données
// Pour ce cas, nous avons introduit une nouvelle fonction readFile, qui lit un json local et renvoie le contenu sous forme de Bytes
const content = readFile('path/to/metadata.json')
handleMetadata(content)
// Maintenant, nous allons tester si un TokenLockMetadata a été créé
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

Ceci 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 de journalisation

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.

Ressources additionnelles

Lien vers cette section

For any additional support, check out this demo subgraph repo using Matchstick.

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
Déploiement en utilisant Subgraph Studio
Modifier une page