Introducing `ic-test` – A Tool for Testing Cross-Chain IC Projects

Hey everyone!

We’re Stan and @ulan, and we’ve been working on a new testing tool called ic-test – a command-line utility designed to simplify testing for cross-chain projects written in Rust. If you’re working with Internet Computer (IC) canisters and optionally EVM-based contracts, this might be something you’ll find useful.

What does ic-test do?

In short, it automates the boilerplate and setup involved in writing canister and cross-chain integration tests:

  • It reads the dfx.json and foundry.toml files to understand your canister and contract setup.
  • It generates Rust types from your Candid .did files.
  • It creates Solidity interfaces from your contract definitions.
  • It sets up the basic API needed to interact with .wasm canisters and .json contracts for test scenarios.
  • It uses pocket-ic and foundry` under the hood to actually run the tests.

How to use it?

  • Install the tool: cargo install ic-test
  • Build your project: dfx build

This ensures dfx pulls all necessary dependencies and generates the .wasm and .did files under the .dfx folder — which ic-test uses to create the test setup.

  • Finally, generate the new test project:
ic-test new tests

This creates a new workspace project called tests. The test setup is saved in ic-test.json.

Inside the tests project you will see the bindings generated for each of the project canisters and EVM contracts. If some of the candid files was changed, you can regenerate the bindings with:

ic-test update

The tests.rs file containing the actual test logic will not be overwritten by default — unless you explicitly run:

ic-test update --force

Example: Hello world test

Create a basic rust canister project:

dfx new hello --type rust

Generate the testing project:

ic-test new

Edit the tests.rs to do the actual testing. Here’s an example tests.rs for a simple “Hello World” project. After some basic adjustments, you might have:

// ...

async fn setup(test: IcpTest) -> Env {
    let icp_user = test.icp.test_user(0);

    let hello_backend = hello_backend::deploy(&icp_user).call().await;

    let hello_frontend = hello_frontend::deploy(
        &icp_user, // init args
        None,
    )
    .call()
    .await;

    // Additional setup steps
    // ...

    Env {
        test,
        hello_backend,
        hello_frontend,
    }
}

#[tokio::test]

async fn test_greet() {
    let Env {
        test,
        hello_backend,
        hello_frontend,
    } = setup(IcpTest::new().await).await;

    // Call the greet method

    let result: String = hello_backend.greet("ic-test".to_owned()).call().await;
    assert_eq!(result, "Hello, ic-test!");
}

Now just run cargo test to see if the test is working.

Example of testing an EVM contract

For a more advanced example involving an EVM contract, check out the Co-processor example on the testing branch. You’ll need foundry installed.

git clone --branch testing https://github.com/wasm-forge/icp-evm-coprocessor-starter

dfx build

forge build

cargo test

:warning: Still a Work in Progress!

The tool is still under active development and it currently not as user-friendly as one might like, so things might change, and some edges might be rough — but that’s exactly why I’m reaching out.

I’d love your feedback!
If you try it out and something doesn’t work, or if there’s a feature you wish it had, please let me know.

Thanks for reading! Looking forward to hearing your thoughts and seeing how you use ic-test in your projects.

Cheers! :waving_hand:

Stan

16 Likes