Attribute Macro test

#[test]
Expand description

Defines an End-to-End test.

The system requirements are:

  • A Substrate node with pallet-revive installed on the local system. You can e.g. use ink-node and install it on your PATH, or provide a path to an executable using the CONTRACTS_NODE environment variable.

Before the test function is invoked the contract will be built. Any errors that occur during the contract build will prevent the test function from being invoked.

§Header Arguments

The #[ink_e2e::test] macro can be provided with additional arguments.

§Custom Environment

You can specify the usage of a custom environment:

#[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)]

Our documentation contains an explainer of what custom environments are. For a full example see here.

§Custom Backend

You can switch the E2E test to use the DRink! testing framework with this syntax:

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test(backend(runtime_only))]
async fn runtime_call_works() -> E2EResult<()> {
    // ...
}

In this configuration the test will not run against a node that is running in the background, but against an in-process slimmed down pallet-revive execution environment.

Please see the page on testing with DRink! in our documentation for more details. For a full example see here.

§Example

#[ink::contract]
mod my_module {
    #[ink(storage)]
    pub struct MyContract {}

    impl MyContract {
        #[ink(constructor)]
        pub fn new() -> Self {
            Self {}
        }

        #[ink(message)]
        pub fn my_message(&self) {}
    }
}

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn e2e_test(mut client: ::ink_e2e::Client<C, E>) -> E2EResult<()> {
    // given
    use my_module::MyContract;
    let mut constructor = MyContract::new();
    let contract = client
        .instantiate("contract_transfer", &ink_e2e::bob(), &mut constructor)
        .submit()
        .await
        .expect("instantiate failed");
    let mut call_builder = contract.call_builder::<MyContract>();

    // when
    let my_message = call_builder.my_message();
    let call_res = client
        .call(&ink_e2e::eve(), &my_message)
        .submit()
        .await
        .expect("call failed");

    // then
    assert!(call_res.is_ok());

    Ok(())
}

You can also build the Keypair type yourself, without going through the pre-defined functions (ink_e2e::alice(), …):

use std::str::FromStr;
let suri = ::ink_e2e::subxt_signer::SecretUri::from_str("//Alice").unwrap();
let alice = ::ink_e2e::Keypair::from_uri(&suri).unwrap();