Docs
La Recherche⌘ K
  • Accueil
  • À propos de The Graph
  • Réseaux pris en charge
  • Contrats du Protocole
  • Subgraphs
    • Substreams
      • Token API
        • AI Suite
          • Indexing
            • Resources
              Subgraphs > Developing > Creating

              28 minutes

              Cadre pour les tests unitaires

              Apprenez à utiliser Matchstick, un cadre de test unitaire développé par LimeChain⁠. Matchstick permet aux développeurs de subgraphs de tester leur logique de mappages dans un environnement sandbox et de déployer avec succès leurs subgraphs.

              Avantages de l’utilisation de Matchstick

              • Il est écrit en Rust et optimisé pour des hautes performances.
              • Il vous donne accès à des fonctions de développement, notamment la possibilité de simuler des appels de contrat, de faire des assertions sur l’état du store, de surveiller les échecs du subgraph, de vérifier les performances des tests, et bien d’autres choses encore.

              Introduction

              Installation des dépendances

              Pour utiliser les méthodes d’aide aux tests et exécuter les tests, vous devez installer les dépendances suivantes :

              1yarn add --dev matchstick-as

              Installer PostgreSQL

              graph-node dépend de PostgreSQL, donc si vous ne l’avez pas déjà, vous devrez l’installer.

              Remarque : Il est fortement recommandé d’utiliser les commandes ci-dessous pour éviter les erreurs inattendues.

              En utilisant MacOS

              Commande d’installation :

              1brew install postgresql

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

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

              En utilisant Linux

              Commande d’installation (dépend de votre distribution) :

              1sudo apt installer postgresql

              En utilisant WSL (Windows Subsystem for Linux)

              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

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

              ou bien

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

              Veuillez vous assurer que vous êtes sur une version plus récente de Node.js graph-cli ne prend plus en charge v10.19.0, et c’est toujours la version par défaut pour les nouvelles images Ubuntu sur le WSL. Par exemple, il est confirmé que Matchstick fonctionne sur WSL avec v18.1.0, vous pouvez passer à cette version via nvm ou si vous mettez à jour votre Node.js global. N’oubliez pas de supprimer node_modules et de relancer npm install après avoir mis à jour votre nodejs ! Ensuite, assurez-vous que libpq est installé, vous pouvez le faire en exécutant

              1sudo apt-get install libpq-dev

              Et enfin, n’utilisez pas graph test (qui utilise votre installation globale de graph-cli et pour une raison quelconque, il semble qu’il soit cassé sur WSL actuellement), utilisez plutôt yarn test ou npm run test (qui utilisera l’instance locale, au niveau du projet, de graph-cli, ce qui fonctionne comme un charme). 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

              1{2  "name": "demo-subgraph",3  "version": "0.1.0",4  "scripts": {5    "test": "graph test",6    ...7  },8  "dependencies": {9    "@graphprotocol/graph-cli": "^0.56.0",10    "@graphprotocol/graph-ts": "^0.31.0",11    "matchstick-as": "^0.6.0"12  }13}

              En utilisant Matchstick

              Pour utiliser Matchstick dans votre projet Subgraph, ouvrez simplement un terminal, naviguez jusqu’au dossier racine de votre projet et lancez 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 flag de source de données n’est spécifié).

              CLI options

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

              1graph test

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

              1gravity graph test

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

              1graph test path/to/file.test.ts

              Options:

              1-c, --coverage                Exécute les tests en mode couverture2-d, --docker                  Exécute les tests dans un conteneur Docker (Note : Exécute à partir du dossier racine du subgraph).3-f, --force                   Binaire : Redécharge le binaire. Docker : Redécharge le fichier Docker et reconstruit l'image Docker.4-h, --help                    Affiche les informations sur l'utilisation5-l, --logs                    Enregistre dans la console des informations sur le système d'exploitation, le modèle de processeur et l'adresse de téléchargement (à des fins de débogage).6-r, --recompile               Oblige à recompiler les tests7-v, --version <tag>           Choisi la version du binaire rust que vous souhaitez télécharger/utiliser

              Docker

              Depuis graph-cli 0.25.2, la commande graph test supporte l’exécution de matchstick dans un conteneur docker avec l’option -d. L’implémentation de docker utilise bind mount⁠ pour ne pas avoir à reconstruire l’image de docker à chaque fois que la commande graph test -d est exécutée. Vous pouvez également suivre les instructions du dépôt matchstick⁠ pour exécuter docker manuellement.

              ❗ graph test -d force docker run à s’exécuter avec le flag -t. Ceci doit être supprimé pour fonctionner dans des environnements non-interactifs (comme GitHub CI).

              ❗ Si vous avez précédemment exécuté graph test, vous pouvez rencontrer l’erreur suivante lors du build de docker :

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

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

              La Configuration

              Matchstick peut être configuré pour utiliser un chemin personnalisé pour les tests, les librairies et les manifestes via le fichier de configuration matchstick.yaml :

              1testsFolder: path/to/tests2libsFolder: path/to/libs3manifestPath: path/to/subgraph.yaml

              Subgraph Demo

              Vous pouvez essayer et jouer avec les exemples de ce guide en clonant le dépôt du Demo Subgraph.⁠

              Tutoriels vidéos

              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

              IMPORTANT : La structure de test décrite ci-dessous dépend de la version de matchstick-as >=0.5.0

              décrivez()

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

              Notes:

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

              L’exemple:

              1import { describe, test } from "matchstick-as/assembly/index"2import { handleNewGravatar } from "../../src/gravity"34describe("handleNewGravatar()", () => {5  test("Il faut créer une nouvelle entité Gravatar", () => {6    ...7  })8})

              Exemple imbriqué de describe() :

              1import { describe, test } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar } from "../../src/gravity"34describe("handleUpdatedGravatar()", () => {5  describe("Lorsque l'entité existe", () => {6    test("met à jour l'entité", () => {7      ...8    })9  })1011  describe("Lorsque l'entité n'existe pas", () => {12    test("il crée une nouvelle entité", () => {13      ...14    })15  })16})

              tester ()

              test(name : String, () =>, should_fail : bool) - Définit un cas de test. Vous pouvez utiliser test() à l’intérieur des blocs describe() ou indépendamment.

              L’exemple:

              1import { describe, test } from "matchstick-as/assembly/index"2import { handleNewGravatar } from "../../src/gravity"34describe("handleNewGravatar()", () => {5  test("Doit créer une nouvelle entité", () => {6    ...7  })8})

              ou bien

              1test("handleNewGravatar() devrait créer une nouvelle entité", () => {2  ...3})

              avantTout()

              Exécute un bloc de code avant tous les tests du fichier. Si beforeAll est déclaré à l’intérieur d’un bloc describe, il s’exécute au début de ce bloc describe.

              Les Exemples:

              Le code contenu dans beforeAll s’exécutera une fois avant tous les tests du fichier.

              1import { describe, test, beforeAll } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"3import { Gravatar } from "../../generated/schema"45beforeAll(() => {6  let gravatar = new Gravatar("0x0")7  gravatar.displayName = “First Gravatar”8  gravatar.save()9  ...10})1112describe("Lorsque l'entité n'existe pas", () => {13  test("il devrait créer un nouveau Gravatar avec l'identifiant 0x1", () => {14    ...15  })16})1718describe("Lorsque l'entité existe déjà", () => {19  test("il devrait mettre à jour le Gravatar avec l'identifiant 0x0", () => {20    ...21  })22})

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

              1import { describe, test, beforeAll } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"3import { Gravatar } from "../../generated/schema"45describe("handleUpdatedGravatar()", () => {6  beforeAll(() => {7    let gravatar = new Gravatar("0x0")8    gravatar.displayName = “First Gravatar”9    gravatar.save()10    ...11  })1213  test("met à jour le Gravatar avec l'identifiant 0x0", () => {14    ...15  })1617  test("crée un nouveau Gravatar avec l'identifiant 0x1", () => {18    ...19  })20})

              afterAll()

              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 s’exécute à la fin de ce bloc describe.

              L’exemple:

              Le code contenu dans afterAll sera exécuté une fois après tous les tests du fichier.

              1import { describe, test, afterAll } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"3import { store } from "@graphprotocol/graph-ts"45afterAll(() => {6  store.remove("Gravatar", "0x0")7  ...8})910describe("handleNewGravatar, () => {11  test("creates Gravatar with id 0x0", () => {12    ...13  })14})1516describe("handleUpdatedGravatar", () => {17  test("updates Gravatar with id 0x0", () => {18    ...19  })20})

              Le code contenu dans afterAll sera exécuté une fois après tous les tests du premier bloc de description

              1import { describe, test, afterAll, clearStore } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"34describe("handleNewGravatar", () => {5	afterAll(() => {6    store.remove("Gravatar", "0x1")7    ...8	})910  test("Il crée une nouvelle entité avec l'identifiant 0x0.", () => {11    ...12  })1314  test("Il crée une nouvelle entité avec l'identifiant 0x1", () => {15    ...16  })17})1819describe("handleUpdatedGravatar", () => {20  test("updates Gravatar with id 0x0", () => {21    ...22  })23})

              beforeEach()

              Exécute 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 dans ce bloc describe.

              Exemples : Le code contenu dans beforeEach sera exécuté avant chaque test.

              1import { describe, test, beforeEach, clearStore } from "matchstick-as/assembly/index"2import { handleNewGravatars } from "./utils"34beforeEach(() => {5  clearStore() // <-- nettoye le store avant chaque test dans le fichier6})78describe("handleNewGravatars, () => {9  test("Un test qui nécessite un store propre", () => {10    ...11  })1213  test("Second, qui nécessite un store propre", () => {14    ...15  })16})1718 ...

              Le code contenu dans beforeEach ne s’exécutera qu’avant chaque test de la description

              1import { describe, test, beforeEach } from 'matchstick-as/assembly/index'2import { handleUpdatedGravatar, handleNewGravatar } from '../../src/gravity'34describe('handleUpdatedGravatars', () => {5  beforeEach(() => {6    let gravatar = new Gravatar('0x0')7    gravatar.displayName = 'First Gravatar'8    gravatar.imageUrl = ''9    gravatar.save()10  })1112  test('Met à jour le nom d'affichage (displayName)', () => {13    assert.fieldEquals('Gravatar', '0x0', 'displayName', 'First Gravatar')1415    // code qui devrait mettre à jour le nom d'affichage (displayName) pour le 1er Gravatar1617    assert.fieldEquals('Gravatar', '0x0', 'displayName', '1st Gravatar')18    store.remove('Gravatar', '0x0')19  })2021  test('Met à jour l'imageUrl', () => {22    assert.fieldEquals('Gravatar', '0x0', 'imageUrl', '')2324    // code qui devrait changer l'imageUrl en https://www.gravatar.com/avatar/0x02526    assert.fieldEquals('Gravatar', '0x0', 'imageUrl', 'https://www.gravatar.com/avatar/0x0')27    store.remove('Gravatar', '0x0')28  })29})

              afterEach()

              Exécute un bloc de code après chaque test. Si afterEach est déclaré à l’intérieur d’un bloc describe, il s’exécute après chaque test dans ce bloc describe.

              Les Exemples:

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

              1import { describe, test, beforeEach, afterEach } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"34beforeEach(() => {5  let gravatar = new Gravatar("0x0")6  gravatar.displayName = “First Gravatar”7  gravatar.save()8})910afterEach(() => {11  store.remove("Gravatar", "0x0")12})1314describe("handleNewGravatar", () => {15  ...16})1718describe("handleUpdatedGravatar", () => {19  test("Met à jour le nom d'affichage (displayName)", () => {20     assert.fieldEquals("Gravatar", "0x0", "displayName", "First Gravatar")2122    // code qui devrait mettre à jour le nom d'affichage (displayName) pour le 1er Gravatar2324    assert.fieldEquals("Gravatar", "0x0", "displayName", "1st Gravatar")25  })2627  test("Met à jour l'imageUrl", () => {28    assert.fieldEquals("Gravatar", "0x0", "imageUrl", "")2930    // code qui devrait changer l'imageUrl en https://www.gravatar.com/avatar/0x03132    assert.fieldEquals("Gravatar", "0x0", "imageUrl", "https://www.gravatar.com/avatar/0x0")33  })34})

              Le code contenu dans afterEach sera exécuté après chaque test de cette description

              1import { describe, test, beforeEach, afterEach } from "matchstick-as/assembly/index"2import { handleUpdatedGravatar, handleNewGravatar } from "../../src/gravity"34describe("handleNewGravatar", () => {5  ...6})78describe("handleUpdatedGravatar", () => {9  beforeEach(() => {10    let gravatar = new Gravatar("0x0")11    gravatar.displayName = "First Gravatar"12    gravatar.imageUrl = ""13    gravatar.save()14  })1516  afterEach(() => {17    store.remove("Gravatar", "0x0")18  })1920  test("Met à jour le nom d'affichage (displayName)", () => {21     assert.fieldEquals("Gravatar", "0x0", "displayName", "First Gravatar")2223    // code qui devrait mettre à jour le nom d'affichage (displayName) pour le 1er Gravatar2425    assert.fieldEquals("Gravatar", "0x0", "displayName", "1st Gravatar")26  })2728  test("Met à jour l'imageUrl", () => {29    assert.fieldEquals("Gravatar", "0x0", "imageUrl", "")3031    // code qui devrait changer l'imageUrl en https://www.gravatar.com/avatar/0x03233    assert.fieldEquals("Gravatar", "0x0", "imageUrl", "https://www.gravatar.com/avatar/0x0")34  })35})

              Assertions

              1fieldEquals(entityType: string, id: string, fieldName: string, expectedVal: string)23equals(expected: ethereum.Value, actual: ethereum.Value)45notInStore(entityType: string, id: string)67addressEquals(address1: Address, address2: Address)89bytesEquals(bytes1: Bytes, bytes2: Bytes)1011i32Equals(number1: i32, number2: i32)1213bigIntEquals(bigInt1: BigInt, bigInt2: BigInt)1415booleanEquals(bool1: boolean, bool2: boolean)1617stringEquals(string1: string, string2: string)1819arrayEquals(array1: Array<ethereum.Value>, array2: Array<ethereum.Value>)2021tupleEquals(tuple1: ethereum.Tuple, tuple2: ethereum.Tuple)2223assertTrue(value: boolean)2425assertNull<T>(value: T)2627assertNotNull<T>(value: T)2829entityCount(entityType: string, expectedCount: i32)

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

              1assert.fieldEquals('Gravatar', '0x123', 'id', '0x123', 'Id doit être 0x123')2assert.equals(ethereum.Value.fromI32(1), ethereum.Value.fromI32(1), 'La valeur doit être égale à 1')3assert.notInStore('Gravatar', '0x124', 'Gravatar ne devrait pas être dans le store')4assert.addressEquals(Address.zero(), Address.zero(), 'L'adresse doit être zéro')5assert.bytesEquals(Bytes.fromUTF8('0x123'), Bytes.fromUTF8('0x123'), 'Les Bytes doivent être égaux')6assert.i32Equals(2, 2, 'I32 doit être égal à 2')7assert.bigIntEquals(BigInt.fromI32(1), BigInt.fromI32(1), 'BigInt doit être égal à 1')8assert.booleanEquals(true, true, 'Le booléen doit être vrai')9assert.stringEquals('1', '1', 'La Chaîne de caractère doit être égale à 1')10assert.arrayEquals([ethereum.Value.fromI32(1)], [ethereum.Value.fromI32(1)], 'Les tableaux doivent être égaux')11assert.tupleEquals(12  changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),13  changetype<ethereum.Tuple>([ethereum.Value.fromI32(1)]),14  'Les tuples doivent être égaux',15)16assert.assertTrue(true, 'Devrait être vrai')17assert.assertNull(null, 'Devrait être null')18assert.assertNotNull('not null', 'Doit être non null')19assert.entityCount('Gravatar', 1, 'Il devrait y avoir 2 gravatars')20assert.dataSourceCount('GraphTokenLockWallet', 1, 'Le modèle GraphTokenLockWallet doit avoir une source de données')21assert.dataSourceExists(22  'GraphTokenLockWallet',23  Adresse.zero().toHexString(),24  'GraphTokenLockWallet doit avoir une source de données pour zéro adresse',25)

              Écrire un test unitaire

              Voyons à quoi ressemblerait un test unitaire simple en utilisant les exemples de Gravatar dans le Subgraph de Démo⁠.

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

              1export function handleNewGravatar(event: NewGravatar): void {2  let gravatar = new Gravatar(event.params.id.toHex())3  gravatar.owner = event.params.owner4  gravatar.displayName = event.params.displayName5  gravatar.imageUrl = event.params.imageUrl6  gravatar.save()7}89export function handleNewGravatars(events: NewGravatar[]): void {10  events.forEach((event) => {11    handleNewGravatar(event)12  })13}1415export function createNewGravatarEvent(16  id: i32,17  ownerAddress: string,18  displayName: string,19  imageUrl: string,20): NewGravatar {21  let mockEvent = newMockEvent()22  let newGravatarEvent = new NewGravatar(23    mockEvent.address,24    mockEvent.logIndex,25    mockEvent.transactionLogIndex,26    mockEvent.logType,27    mockEvent.block,28    mockEvent.transaction,29    mockEvent.parameters,30  )31  newGravatarEvent.parameters = new Array()32  let idParam = new ethereum.EventParam('id', ethereum.Value.fromI32(id))33  let addressParam = new ethereum.EventParam(34    'ownerAddress',35    ethereum.Value.fromAddress(Address.fromString(ownerAddress)),36  )37  let displayNameParam = new ethereum.EventParam('displayName', ethereum.Value.fromString(displayName))38  let imageUrlParam = new ethereum.EventParam('imageUrl', ethereum.Value.fromString(imageUrl))3940  newGravatarEvent.parameters.push(idParam)41  newGravatarEvent.parameters.push(addressParam)42  newGravatarEvent.parameters.push(displayNameParam)43  newGravatarEvent.parameters.push(imageUrlParam)4445  return newGravatarEvent46}

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

              1import { clearStore, test, assert } from 'matchstick-as/assembly/index'2import { Gravatar } from '../../generated/schema'3import { NewGravatar } from '../../generated/Gravity/Gravity'4import { createNewGravatarEvent, handleNewGravatars } from '../mappings/gravity'56test('Possibilité d'appeler des mappages avec des événements personnalisés', () => {7  // Créer une entité de test et la sauvegarder dans le store en tant qu'état initial (optionnel)8  let gravatar = new Gravatar('gravatarId0')9  gravatar.save()1011  // Créer des événements fictifs12  let newGravatarEvent = createNewGravatarEvent(12345, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')13  let anotherGravatarEvent = createNewGravatarEvent(3546, '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7', 'cap', 'pac')1415  // Appeler les fonctions de mappage en passant par les événements que nous venons de créer16  handleNewGravatars([newGravatarEvent, anotherGravatarEvent])1718  // Affirmer l'état du store19  assert.fieldEquals('Gravatar', 'gravatarId0', 'id', 'gravatarId0')20  assert.fieldEquals('Gravatar', '12345', 'owner', '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7')21  assert.fieldEquals('Gravatar', '3546', 'displayName', 'cap')2223  // Effacer le store afin de commencer le prochain test sur une ardoise propre24  clearStore()25})2627test('Next test', () => {28  //...29})

              Cela fait beaucoup à décortiquer ! Tout d’abord, une chose importante à noter est que nous importons des choses à partir de matchstick-as, notre bibliothèque d’aide AssemblyScript (distribuée comme un 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 ;
              • Nous définissons deux objets d’événement NewGravatar avec leurs données, en utilisant la fonction createNewGravatarEvent() ;
              • Nous appelons les méthodes de gestion de 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 ;
              • Enfin, nous nettoyons le store en utilisant clearStore() afin que notre prochain test puisse commencer avec un objet de store 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, pour exécuter nos tests, il vous suffit d’exécuter ce qui suit dans le dossier racine de Subgraph :

              graph test Gravity

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

              Matchstick avec le message “Tous les tests sont réussis!”

              Scénarios de tests actuels

              L’Hydratation du magasin avec un certain état

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

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

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

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

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

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

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

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

              Appels de contrat moqueurs

              Les utilisateurs peuvent simuler des appels de contrat :

              1import { addMetadata, assert, createMockedFunction, clearStore, test } from 'matchstick-as/assembly/index'2import { Gravity } from '../../generated/Gravity/Gravity'3import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'45let contractAddress = Address.fromString('0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7')6let expectedResult = Address.fromString('0x90cBa2Bbb19ecc291A12066Fd8329D65FA1f1947')7let bigIntParam = BigInt.fromString('1234')8createMockedFunction(contractAddress, 'gravatarToOwner', 'gravatarToOwner(uint256):(address)')9  .withArgs([ethereum.Value.fromSignedBigInt(bigIntParam)])10  .returns([ethereum.Value.fromAddress(Address.fromString('0x90cBa2Bbb19ecc291A12066Fd8329D65FA1f1947'))])1112let gravity = Gravity.bind(contractAddress)13let result = gravity.gravatarToOwner(bigIntParam)1415assert.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 :

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

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

              Les utilisateurs peuvent simuler des fichiers IPFS en utilisant la fonction mockIpfsFile(hash, filePath). La fonction accepte deux arguments, le premier étant le hash/chemin du fichier IPFS et le second le chemin d’un fichier local.

              NOTE : Lorsque l’on teste ipfs.map/ipfs.mapJSON, la fonction callback doit être exportée depuis le fichier de test afin que matchtick la détecte, comme la fonction processGravatar() dans l’exemple de test ci-dessous :

              Ficher .test.ts :

              1import { assert, test, mockIpfsFile } from 'matchstick-as/assembly/index'2import { ipfs } from '@graphprotocol/graph-ts'3import { gravatarFromIpfs } from './utils'45// Exporter le callback ipfs.map() pour qu'il soit détecté par matchstick6export { processGravatar } from './utils'78test('ipfs.cat', () => {9  mockIpfsFile('ipfsCatfileHash', 'tests/ipfs/cat.json')1011  assert.entityCount(GRAVATAR_ENTITY_TYPE, 0)1213  gravatarFromIpfs()1415  assert.entityCount(GRAVATAR_ENTITY_TYPE, 1)16  assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '1', 'imageUrl', 'https://i.ytimg.com/vi/MELP46s8Cic/maxresdefault.jpg')1718  clearStore()19})2021test('ipfs.map', () => {22  mockIpfsFile('ipfsMapfileHash', 'tests/ipfs/map.json')2324  assert.entityCount(GRAVATAR_ENTITY_TYPE, 0)2526  ipfs.map('ipfsMapfileHash', 'processGravatar', Value.fromString('Gravatar'), ['json'])2728  assert.entityCount(GRAVATAR_ENTITY_TYPE, 3)29  assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '1', 'displayName', 'Gravatar1')30  assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '2', 'displayName', 'Gravatar2')31  assert.fieldEquals(GRAVATAR_ENTITY_TYPE, '3', 'displayName', 'Gravatar3')32})

              Fichier utils.ts :

              1import { Address, ethereum, JSONValue, Value, ipfs, json, Bytes } from "@graphprotocol/graph-ts"2import { Gravatar } from "../../generated/schema"34...56// rappel ipfs.map7export function processGravatar(value: JSONValue, userData: Value): void {8  // Consultez la documentation de JSONValue pour plus de détails sur la façon de traiter les données.9  // avec JSON values10  let obj = value.toObject()11  let id = obj.get('id')1213  if (!id) {14    return15  }1617  // Des entités de rappel peuvent également être créées18  let gravatar = new Gravatar(id.toString())19  gravatar.displayName = userData.toString() + id.toString()20  gravatar.save()21}2223// fonction qui appelle ipfs.cat24export function gravatarFromIpfs(): void {25  let rawData = ipfs.cat("ipfsCatfileHash")2627  if (!rawData) {28    return29  }3031  let jsonData = json.fromBytes(rawData as Bytes).toObject()3233  let id = jsonData.get('id')34  let url = jsonData.get("imageUrl")3536  if (!id || !url) {37    return38  }3940  let gravatar = new Gravatar(id.toString())41  gravatar.imageUrl = url.toString()42  gravatar.save()43}

              Affirmation de l’état du magasin

              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:

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

              L’exécution de la fonction assert.fieldEquals() permet de vérifier l’égalité du champ donné par rapport à la valeur attendue donnée. Le test échouera et un message d’erreur sera affiché si les valeurs NE SONT PAS égales. Dans le cas contraire, le test passera avec succès.

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

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

              1// Lisez2let logType = newGravatarEvent.logType34// Écrivez5let UPDATED_ADDRESS = '0xB16081F360e3847006dB660bae1c6d1b2e17eC2A'6newGravatarEvent.address = Address.fromString(UPDATED_ADDRESS)

              Affirmation de l’égalité des variables

              1assert.equals(ethereum.Value.fromString("bonjour"); ethereum.Value.fromString("bonjour"));

              Affirmer qu’une entité n’est PAS dans le store

              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é :

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

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

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

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

              Depuis la version 0.6.0, logStore n’affiche plus les champs dérivés, au lieu de celà les utilisateurs peuvent utiliser la nouvelle fonction logEntity. Bien sûr, logEntity peut être utilisée pour afficher n’importe quelle entité, pas seulement celles qui ont des champs dérivés. logEntity prend le type d’entité, l’identifiant de l’entité et un flag showRelated pour indiquer si les utilisateurs veulent afficher les entités dérivées associées.

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

              Échec prévu

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

              1test(2  'Devrait générer une erreur',3  () => {4    throw new Error()5  },6  true,7)

              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

              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 :

              1import { test } from "matchstick-as/assembly/index";2import { log } from "matchstick-as/assembly/log";34test("Success", () => {5    log.success("Succès!". []);6});7test("Error", () => {8    log.error("Erreur :( ", []);9});10test("Debug", () => {11    log.debug("Deboggage...", []);12});13test("Info", () => {14    log.info("Info!", []);15});16test("Warning", () => {17    log.warning("Avertissement!", []);18});

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

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

              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

              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 y accédant en tant que champs/propriétés d’entité, comme suit :

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

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

              1test('Derived fields example test', () => {2  let mainAccount = GraphAccount.load('12')!34  assert.assertNull(mainAccount.get('nameSignalTransactions'))5  assert.assertNull(mainAccount.get('operatorOf'))67  let operatedAccount = GraphAccount.load('1')!8  operatedAccount.operators = [mainAccount.id]9  operatedAccount.save()1011  mockNameSignalTransaction('1234', mainAccount.id)12  mockNameSignalTransaction('2', mainAccount.id)1314  mainAccount = GraphAccount.load('12')!1516  assert.assertNull(mainAccount.get('nameSignalTransactions'))17  assert.assertNull(mainAccount.get('operatorOf'))1819  const nameSignalTransactions = mainAccount.nameSignalTransactions.load()20  const operatorsOfMainAccount = mainAccount.operatorOf.load()2122  assert.i32Equals(2, nameSignalTransactions.length)23  assert.i32Equals(1, operatorsOfMainAccount.length)2425  assert.stringEquals('1', operatorsOfMainAccount[0].id)2627  mockNameSignalTransaction('2345', mainAccount.id)2829  let nst = NameSignalTransaction.load('1234')!30  nst.signer = '11'31  nst.save()3233  store.remove('NameSignalTransaction', '2')3435  mainAccount = GraphAccount.load('12')!36  assert.i32Equals(1, mainAccount.nameSignalTransactions.load().length)37})

              Test de loadInBlock

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

              1import { afterAll, beforeAll, describe, mockInBlockStore, test } from 'matchstick-as'2import { Gravatar } from '../../generated/schema'34describe('loadInBlock', () => {5  beforeAll(() => {6    mockInBlockStore('Gravatar', 'gravatarId0', gravatar)7  })89  afterAll(() => {10    clearInBlockStore()11  })1213  test('Peut utiliser entity.loadInBlock() pour récupérer l'entité dans le sore du cache du bloc actuel', () => {14    let retrievedGravatar = Gravatar.loadInBlock('gravatarId0')15    assert.stringEquals('gravatarId0', retrievedGravatar!.get('id')!.toString())16  })1718  test("Renvoit null lors de l'appel à entity.loadInBlock() si une entité n'existe pas dans le bloc actuel", () => {19    let retrievedGravatar = Gravatar.loadInBlock('IDoNotExist')20    assert.assertNull(retrievedGravatar)21  })22})

              Tester les sources de données dynamiques

              Le test des sources de données dynamiques peut être effectué en simulant la valeur de retour des fonctions context(), address() et network() du namespace dataSource. Ces fonctions renvoient actuellement les valeurs suivantes context() - renvoit une entité vide (DataSourceContext), address() - renvoit 0x00000000000000000000000000000000, network() - renvoit mainnet. Les fonctions create(...) et createWithContext(...) sont simulées pour ne rien faire, donc elles n’ont pas besoin d’être appelées dans les tests. Les modifications des valeurs de retour peuvent être faites à travers les fonctions du namespacedataSourceMock 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) :

              1export function handleApproveTokenDestinations(event: ApproveTokenDestinations): void {2  let tokenLockWallet = TokenLockWallet.load(dataSource.address().toHexString())!3  if (dataSource.network() == 'rinkeby') {4    tokenLockWallet.tokenDestinationsApproved = true5  }6  let context = dataSource.context()7  if (context.get('contextVal')!.toI32() > 0) {8    tokenLockWallet.setBigInt('tokensReleased', BigInt.fromI32(context.get('contextVal')!.toI32()))9  }10  tokenLockWallet.save()11}

              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 :

              1import { assert, test, newMockEvent, dataSourceMock } from 'matchstick-as/assembly/index'2import { BigInt, DataSourceContext, Value } from '@graphprotocol/graph-ts'34import { handleApproveTokenDestinations } from '../../src/token-lock-wallet'5import { ApproveTokenDestinations } from '../../generated/templates/GraphTokenLockWallet/GraphTokenLockWallet'6import { TokenLockWallet } from '../../generated/schema'78test('Source de données : simple exemple de simulation ', () => {9  let addressString = '0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'10  let address = Address.fromString(addressString)1112  let wallet = new TokenLockWallet(address.toHexString())13  wallet.save()14  let context = new DataSourceContext()15  context.set('contextVal', Value.fromI32(325))16  dataSourceMock.setReturnValues(addressString, 'rinkeby', context)17  let event = changetype<ApproveTokenDestinations>(newMockEvent())1819  assert.assertTrue(!wallet.tokenDestinationsApproved)2021  handleApproveTokenDestinations(event)2223  wallet = TokenLockWallet.load(address.toHexString())!24  assert.assertTrue(wallet.tokenDestinationsApproved)25  assert.bigIntEquals(wallet.tokensReleased, BigInt.fromI32(325))2627  dataSourceMock.resetValues()28})

              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

              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 à la fois les modèles ethereum/contract et file/ipfs. Il y a quatre fonctions pour cela :

              • assert.dataSourceCount(templateName, expectedCount) peut être utilisé 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 du modèle spécifié sur la console à des fins de débogage
              • readFile(path) lit un fichier JSON qui représente un fichier IPFS et renvoie le contenu sous forme d’octets

              Test des modèles ethereum/contrat

              1test('ethereum/contract dataSource creation example', () => {2  // Affirmer qu'il n'y a pas de dataSources créées à partir du modèle GraphTokenLockWallet3  assert.dataSourceCount('GraphTokenLockWallet', 0)45  // Créer une nouvelle source de données GraphTokenLockWallet avec l'adresse 0xA16081F360e3847006dB660bae1c6d1b2e17eC2A6  GraphTokenLockWallet.create(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'))78  // Affirmer que la source de données a été créée9  assert.dataSourceCount('GraphTokenLockWallet', 1)1011  // Ajouter une deuxième source de données avec le contexte12  let context = new DataSourceContext()13  context.set('contextVal', Value.fromI32(325))1415  GraphTokenLockWallet.createWithContext(Address.fromString('0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'), context)1617  // Affirmer qu'il y a maintenant 2 sources de données18  assert.dataSourceCount('GraphTokenLockWallet', 2)1920  // Affirme qu'une source de données avec l'adresse "0xA16081F360e3847006dB660bae1c6d1b2e17eC2B" a été créée.21  // Gardez à l'esprit que le type `Address` est transformé en minuscules lorsqu'il est décodé, vous devez donc passer l'adresse en minuscules lorsque vous affirmez qu'elle existe.22  assert.dataSourceExists('GraphTokenLockWallet', '0xA16081F360e3847006dB660bae1c6d1b2e17eC2B'.toLowerCase())2324  logDataSources('GraphTokenLockWallet')25})
              Exemple de sortie logDataSource
              1🛠  {2  "0xa16081f360e3847006db660bae1c6d1b2e17ec2a": {3    "kind": "ethereum/contract",4    "name": "GraphTokenLockWallet",5    "address": "0xa16081f360e3847006db660bae1c6d1b2e17ec2a",6    "context": null7  },8  "0xa16081f360e3847006db660bae1c6d1b2e17ec2b": {9    "kind": "ethereum/contract",10    "name": "GraphTokenLockWallet",11    "address": "0xa16081f360e3847006db660bae1c6d1b2e17ec2b",12    "context": {13      "contextVal": {14        "type": "Int",15        "data": 32516      }17    }18  }

              Test des modèles file/ipfs

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

              Exemple subgraph.yaml
              1...2templates:3 - kind: file/ipfs4    name: GraphTokenLockMetadata5    network: mainnet6    mapping:7      kind: ethereum/events8      apiVersion: 0.0.99      language: wasm/assemblyscript10      file: ./src/token-lock-wallet.ts11      handler: handleMetadata12      entities:13        - TokenLockMetadata14      abis:15        - name: GraphTokenLockWallet16          file: ./abis/GraphTokenLockWallet.json
              Exemple schema.graphql
              1"""2Portefeuilles de verrouillage de jetons qui contiennent des GRT verrouillés3"""4type TokenLockMetadata @entity {5  "L'adresse du portefeuille de blocage des jetons"6  id: ID!7  "Heure de début du calendrier de sortie"8  startTime: BigInt!9  "Heure de fin du calendrier de sortie""10  endTime: BigInt!11  "Nombre de périodes entre l'heure de début et l'heure de fin"12  periods: BigInt!13  "Heure à laquelle commence la sortie"14  releaseStartTime: BigInt!15}
              Exemple metadata.json
              1{2  "startTime": 1,3  "endTime": 1,4  "periods": 1,5  "releaseStartTime": 16}
              Exemple de gestionnaire
              1export function handleMetadata(content: Bytes): void {2  // dataSource.stringParams() renvoie le CID du fichier de la source de données3  // stringParam() sera simulé dans le test du gestionnaire4  // pour plus d'informations https://thegraph.com/docs/en/developing/creating-a-subgraph/#create-a-new-handler-to-process-files5  let tokenMetadata = new TokenLockMetadata(dataSource.stringParam())6  const value = json.fromBytes(content).toObject()78  if (value) {9    const startTime = value.get('startTime')10    const endTime = value.get('endTime')11    const periods = value.get('periods')12    const releaseStartTime = value.get('releaseStartTime')1314    if (startTime && endTime && periods && releaseStartTime) {15      tokenMetadata.startTime = startTime.toBigInt()16      tokenMetadata.endTime = endTime.toBigInt()17      tokenMetadata.periods = periods.toBigInt()18      tokenMetadata.releaseStartTime = releaseStartTime.toBigInt()19    }2021    tokenMetadata.save()22  }23}
              Exemple de test
              1import { assert, test, dataSourceMock, readFile } from 'matchstick-as'2import { Address, BigInt, Bytes, DataSourceContext, ipfs, json, store, Value } from '@graphprotocol/graph-ts'34import { handleMetadata } from '../../src/token-lock-wallet'5import { TokenLockMetadata } from '../../generated/schema'6import { GraphTokenLockMetadata } from '../../generated/templates'78test('exemple de création d'une dataSource file/ipfs', () => {9  // Générer le CID de la source de données à partir du fichier ipfsHash + chemin ipfs10  // Par exemple QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm/example.json11  const ipfshash = 'QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm'12  const CID = `${ipfshash}/example.json`1314  // Créer une nouvelle dataSource en utilisant le CID généré15  GraphTokenLockMetadata.create(CID)1617  // Affirmer que la dataSource a été créée18  assert.dataSourceCount('GraphTokenLockMetadata', 1)19  assert.dataSourceExists('GraphTokenLockMetadata', CID)20  logDataSources('GraphTokenLockMetadata')2122  // Nous devons maintenant simuler les métadonnées de la dataSource et plus particulièrement dataSource.stringParam()23  // dataSource.stringParams utilise en fait la valeur de dataSource.address(), nous allons donc simuler l'adresse en utilisant dataSourceMock de matchstick-as24  // Nous allons d'abord réinitialiser les valeurs, puis utiliser dataSourceMock.setAddress() pour définir le CID.25  dataSourceMock.resetValues()26  dataSourceMock.setAddress(CID)272829  // Nous devons maintenant générer les octets à transmettre au gestionnaire de la dataSource.30  // Pour ce cas, nous avons introduit une nouvelle fonction readFile, qui lit un json local et renvoie le contenu sous forme d'octets31  const content = readFile(`path/to/metadata.json`)32  handleMetadata(content)3334  // Maintenant nous allons tester si un TokenLockMetadata a été créé35  const metadata = TokenLockMetadata.load(CID)3637  assert.bigIntEquals(metadata !.endTime, BigInt.fromI32(1))38  assert.bigIntEquals(metadata !.periods, BigInt.fromI32(1))39  assert.bigIntEquals(metadata !.releaseStartTime, BigInt.fromI32(1))40  assert.bigIntEquals(metadata !.startTime, BigInt.fromI32(1))41})

              Couverture de test

              En utilisant Matchstick, les développeurs de Subgraph peuvent exécuter un script qui calculera la couverture des tests unitaires écrits.

              L’outil de couverture des tests prend les binaires de test compilés wasm et les convertit en fichiers wat, qui peuvent alors être facilement inspectés pour voir si les gestionnaires définis dans subgraph.yaml ont été appelés ou non. Comme la couverture du code (et les tests dans leur ensemble) n’en est qu’à ses débuts en AssemblyScript et WebAssembly, Matchstick ne peut pas vérifier la couverture des branches. Au lieu de cela, nous nous appuyons sur l’affirmation que si un gestionnaire donné a été appelé, l’événement/la fonction correspondant(e) a été correctement simulé(e).

              Prérequis

              Pour utiliser la fonctionnalité de couverture des tests fournie dans Matchstick, il y a quelques éléments à préparer à l’avance :

              Exportez vos gestionnaires

              Pour que Matchstick puisse vérifier quels handlers sont exécutés, ces handlers doivent être exportés depuis le fichier de test. Ainsi, dans notre exemple, dans notre fichier gravity.test.ts, nous avons importé le gestionnaire suivant :

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

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

              1exportez { handleNewGravatar }

              Usage

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

              1graph test -- -c

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

              1"scripts": {2    /.../3    "coverage": "graph test -- -c"4  },

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

              1$ graph test -c2Skipping download/install step because binary already exists at /Users/petko/work/demo-subgraph/node_modules/binary-install-raw/bin/0.4.034___  ___      _       _         _   _      _5|  \/  |     | |     | |       | | (_)    | |6| .  . | __ _| |_ ___| |__  ___| |_ _  ___| | __7| |\/| |/ _` | __/ __| '_ \/ __| __| |/ __| |/ /8| |  | | (_| | || (__| | | \__ \ |_| | (__|   <9\_|  |_/\__,_|\__\___|_| |_|___/\__|_|\___|_|\_\1011Compiling...1213Running in coverage report mode.14 ️15Reading generated test modules... 🔎️1617Generating coverage report 📝1819Handlers for source 'Gravity':20Handler 'handleNewGravatar' is tested.21Handler 'handleUpdatedGravatar' is not tested.22Handler 'handleCreateGravatar' is tested.23Test coverage: 66.7% (2/3 handlers).2425Handlers for source 'GraphTokenLockWallet':26Handler 'handleTokensReleased' is not tested.27Handler 'handleTokensWithdrawn' is not tested.28Handler 'handleTokensRevoked' is not tested.29Handler 'handleManagerUpdated' is not tested.30Handler 'handleApproveTokenDestinations' is not tested.31Handler 'handleRevokeTokenDestinations' is not tested.32Test coverage: 0.0% (0/6 handlers).3334Global test coverage: 22.2% (2/9 handlers).

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

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

              [Thu, 31 Mar 2022 13:54:54 +0300] Program executed in: 42.270ms.

              Erreurs de compilation courantes

              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, qui n’est pas pris en charge par AssemblyScript. Veuillez considérer l’utilisation de Logging API

              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)

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

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

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

              La non-concordance des arguments est causée par la non-concordance de graph-ts et de 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 supplémentaires

              Pour toute aide supplémentaire, consultez cette démo Subgraph repo utilisant Matchstick⁠.

              Réaction

              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.

              ⁠Edit on GitHub⁠

              ChangelogDéploiement en utilisant Subgraph Studio
              On this page
              • Avantages de l’utilisation de Matchstick
              • Introduction
              • Installation des dépendances
              • Installer PostgreSQL
              • En utilisant WSL (Windows Subsystem for Linux)
              • En utilisant Matchstick
              • CLI options
              • Docker
              • La Configuration
              • Subgraph Demo
              • Tutoriels vidéos
              • Structure des tests
              • décrivez()
              • tester ()
              • avantTout()
              • afterAll()
              • beforeEach()
              • afterEach()
              • Assertions
              • Écrire un test unitaire
              • Scénarios de tests actuels
              • L’Hydratation du magasin avec un certain état
              • Appel d’une fonction de cartographie avec un événement
              • Appel de tous les mappages avec des projecteurs d’événements
              • Appels de contrat moqueurs
              • Se moquer des fichiers IPFS (à partir de Matchstick 0.4.1)
              • Affirmation de l’état du magasin
              • Interagir avec les métadonnées d’événement
              • Affirmation de l’égalité des variables
              • Affirmer qu’une entité n’est PAS dans le store
              • Affichage de tout le magasin ou d’entités individuelles (à des fins de débogage)
              • Échec prévu
              • Journal de bord
              • Tests dérivés
              • Test de loadInBlock
              • Tester les sources de données dynamiques
              • Test de la création dynamique de sources de données
              • Couverture de test
              • Prérequis
              • Usage
              • Durée d’exécution du test dans la sortie du journal
              • Erreurs de compilation courantes
              • Ressources supplémentaires
              • Réaction
              The GraphStatusTestnetActifs de la MarqueForumSécuritéPolitique de confidentialitéConditions d'utilisation