Testing with Rust

If you look again at the project structure when you create a new Forc project with forc init, you can see a directory called tests/:

$ forc init my-fuel-project
$ cd my-fuel-project
$ tree .
├── Cargo.toml
├── Forc.toml
├── src
│   └── main.sw
└── tests
    └── harness.rs

Note that this is also a Rust package, hence the existence of a Cargo.toml (Rust manifest file) in the project root directory. The Cargo.toml in the root directory contains necessary Rust dependencies to enable you to write Rust-based tests using our Rust SDK, (fuels-rs).

These tests can be run using forc test which will look for Rust tests under the tests/ directory (created automatically with forc init).

For example, let's write tests against the following contract, written in Sway. This can be done in the pregenerated src/main.sw or in a new file in src. In the case of the latter, update the entry field in Forc.toml to point at the new contract.

contract;

abi TestContract {
    fn initialize_counter(value: u64) -> u64;
    fn increment_counter(amount: u64) -> u64;
}

storage {
    counter: u64,
}

impl TestContract for Contract {
    fn initialize_counter(value: u64) -> u64 {
        storage.counter = value;
        value
    }

    fn increment_counter(amount: u64) -> u64 {
        let incremented = storage.counter + amount;
        storage.counter = incremented;
        incremented
    }
}

Our tests/harness.rs file could look like:

use fuel_tx::{ContractId, Salt};
use fuels::prelude::*;
use fuels::test_helpers;
use fuels_abigen_macro::abigen;

// Load abi from json
abigen!(MyContract, "out/debug/my-fuel-project-abi.json");

async fn get_contract_instance() -> (MyContract, ContractId) {
    // Deploy the compiled contract
    let salt = Salt::from([0u8; 32]);
    let compiled = Contract::load_sway_contract("./out/debug/my-fuel-project.bin", salt).unwrap();

    // Launch a local network and deploy the contract
    let (provider, wallet) = test_helpers::setup_test_provider_and_wallet().await;

    let id = Contract::deploy(&compiled, &provider, &wallet, TxParameters::default())
        .await
        .unwrap();

    let instance = MyContract::new(id.to_string(), provider, wallet);

    (instance, id)
}

#[tokio::test]
async fn can_get_contract_id() {
    let (contract_instance, _id) = get_contract_instance().await;

    // Call `initialize_counter()` method in our deployed contract.
    // Note that, here, you get type-safety for free!
    let result = contract_instance
        .initialize_counter(42)
        .call()
        .await
        .unwrap();

    assert_eq!(42, result.value);

    // Call `increment_counter()` method in our deployed contract.
    let result = contract_instance
        .increment_counter(10)
        .call()
        .await
        .unwrap();

    assert_eq!(52, result.value);
    // Now you have an instance of your contract you can use to test each function
}

Then, in the root of our project, running forc test will run the test above, compiling and deploying the contract to a local Fuel network, and calling the ABI methods against the contract deployed in there:

$ forc test

running 1 test
test harness ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.64s