Skip to main content

Integration Tests with Mainnet Data

Testing with mainnet data is another form of integration test, where not only mainnet contracts can be used, but also their data on mainnet.

Our ability to test a contract that relies on another contract is limited to our own knowledge of this other contract and the different states it can get into. Integration testing, where real dependencies are used removes some assumptions, but if a dependency has complex state it may also be useful to test against mainnet data as it exists at different points in time.

Testing with mainnet data is one way to further close the gap.

The Soroban Rust SDK and Stellar CLI come together to make possible testing with mainnet data.

How to Write Tests with Mainnet Data​

The following is an example of a test that includes a dependency contract into the test, rather than mock it. The test is written to test the increment-with-pause contract and the pause contract. The contract has an increment function that increases a counter value by one on every invocation. The contract depends on the pause contract to control whether the increment functionality is paused.

The following tests set up the increment-with-pause contract, using a ledger snapshot that has the dependency pause contract already deployed along with it's contract data.

  1. Use [stellar snapshot create] command to create a snapshot of the pause contract and its data.

    stellar snapshot create --address C... --output json --out snapshot.json
    info

    The --ledger <ledger-seq> option can be added to specify a ledger to snapshot. When not specified the latest ledger that's been pushed to archives is snapshot, that is usually less than 5 minutes old.

  2. Use Env::from_ledger_snapshot_file(...) to load the snapshot into an Env in the test.

    let env = Env::from_ledger_snapshot_file("snapshot.json");
  3. Write a test similar to the following that uses the Env preloaded with the contracts and contract data in snapshot.json. The test makes assertions about the behavior of the main contract in the context of the real dependencies and data that the dependencies have on the network.

    #[test]
    fn test() {
    let env = Env::from_ledger_snapshot_file("snapshot.json");

    let contract_id = env.register(
    IncrementContract,
    IncrementContractArgs::__constructor(&pause_id),
    );
    let client = IncrementContractClient::new(&env, &contract_id);

    assert_eq!(client.increment(), 1);
    }

Most tests, whether they're unit, mocks, or integration tests, will look very similar to the test above. The tests will do four things:

  1. Create an environment, the Env, either with Env::default() or Env::from_ledger_snapshot_file(...).
  2. Register the contract(s) to be tested.
  3. Invoke functions using the generated client.
  4. Assert the outcome.
tip

Snapshots created from testnet or mainnet can also be a way to test and debug incidents with a contract on network. A snapshot can be created at a specific ledger and tests written using that snapshot to better understand why or how a series of events occurred.

tip

A snapshot can be created of any contract deployed to mainnet or testnet using the stellar snapshot create command. It can be a fun way to experiment with and learn about deployed contracts by loading a snapshot into a test and invoking the contract. Fuzzing can even be performed on contracts using this tooling.