Testing the subgraph is a complicated endeavour given the amount of statefulness in subgraphs and lack of testing tools in the space.
The goal is to test the various mapping handlers to make sure the state mutations they emit are valid and accurate. Some mapping handlers may perform differently when they are tested atomically vs. being tested against live state, so please be thoughtful about the kinds of tests you create.
You need 4 sets of information in order to conduct a single test:
- The state of the blockchain at height N
- The state of the subgraph at height N-1 (before the test takes place)
- An Event or Call from block N that will cause a mutation in the subgraph's state
- The state of the subgraph at height N (your test will assert that its ending state matches this)
Blockchain state is provided by fixtures in the fixture directory. Each file contains a single fixture that reproduces various parts of blockchain state using Matchstick's createMockedFunction
.
Since it is not viable to store the state of the entire blockchain in each fixture, the only state that is included is the state required to execute the associated test. Some fixtures are used by multiple tests, so may contain mocked functions that are not relevant to every specific test that uses it.
While fixtures can be manually built, it is a huge time sink. An easier alternative is to generate fixtures automatically using FixtureGenerator.
FixtureGenerator generates fixtures based off addresses, ABIs, and block heights that are fed into it. You must provide FixtureGenerator with an archive node endpoint since the state it needs to query is often pruned on non-archive nodes. FixtureGenerator can be invoked using yarn build:fixtures
.
Keep in mind these fixtures are limited to mocking contract state, graph entities cannot be mocked by fixtures (yet).
This is where things get tricky. There is currently no way to mock subgraph state. Subgraph state must be constructed by combining various chain state fixtures and mapping handlers to build the state.
The first test in strategy.test.ts
is a good example. In order to test the mapping handler handleAddStrategyV1
, the subgraph's state must already contain entities for the Registry and Vault. To prepare for testing the handler, a mock Registry entitiy is created using createRegistryV1Entity
, then a chain state fixture is set up so the Vault entity can be created using the handleNewVaultInner handler.
Event and calldata params can be obtained from Etherscan. Functions for generating mock events/calls can be found under the utils directory.
This is the intended final state of the subgraph that is being tested. At the end of each test, a series of assertations is used to verify that the state generated by the subgraph matches what you are expecting.
The easiest way to write tests is to locate a Vault on mainnet that triggers the handler you'd like to test. If you can't find an example of your test case on mainnet, you can also manually construct the chain state required for the test.
New chain state fixtures can be added in fixtureGenerator/main.ts
.