Skip to content

Mock Runtime

Introduction

Testing is essential in Polkadot SDK development to ensure your blockchain operates as intended and effectively handles various potential scenarios. This guide walks you through setting up an environment to test pallets within the runtime, allowing you to evaluate how different pallets, their configurations, and system components interact to ensure reliable blockchain functionality.

Configuring a Mock Runtime

Testing Module

The mock runtime includes all the necessary pallets and configurations needed for testing. To ensure proper testing, you must create a module that integrates all components, enabling assessment of interactions between pallets and system elements.

Here's a simple example of how to create a testing module that simulates these interactions:

pub mod tests {
    use crate::*;
    // ...
}

Note

The crate::*; snippet imports all the components from your crate (including runtime configurations, pallet modules, and utility functions) into the tests module. This allows you to write tests without manually importing each piece, making the code more concise and readable.

Alternatively, you can create a separate mock.rs file to define the configuration for your mock runtime and a companion tests.rs file to house the specific logic for each test.

Once the testing module is configured, you can craft your mock runtime using the frame_support::runtime macro. This macro allows you to define a runtime environment that will be created for testing purposes:

pub mod tests {
    use crate::*;

    #[frame_support::runtime]
    mod runtime {
        #[runtime::runtime]
        #[runtime::derive(
            RuntimeCall,
            RuntimeEvent,
            RuntimeError,
            RuntimeOrigin,
            RuntimeFreezeReason,
            RuntimeHoldReason,
            RuntimeSlashReason,
            RuntimeLockId,
            RuntimeTask
        )]
        pub struct Test;

        #[runtime::pallet_index(0)]
        pub type System = frame_system::Pallet<Test>;

        // Other pallets...
    }
}

Genesis Storage

The next step is configuring the genesis storage—the initial state of your runtime. Genesis storage sets the starting conditions for the runtime, defining how pallets are configured before any blocks are produced. You can only customize the initial state only of those items that implement the [pallet::genesis_config] and [pallet::genesis_build] macros within their respective pallets.

In Polkadot SDK, you can create this storage using the BuildStorage trait from the sp_runtime crate. This trait is essential for building the configuration that initializes the blockchain's state.

The function new_test_ext() demonstrates setting up this environment. It uses frame_system::GenesisConfig::<Test>::default() to generate a default genesis configuration for the runtime, followed by .build_storage() to create the initial storage state. This storage is then converted into a format usable by the testing framework, sp_io::TestExternalities, allowing tests to be executed in a simulated blockchain environment.

Here's the code that sets the genesis storage configuration:

pub mod tests {
    use crate::*;
    use sp_runtime::BuildStorage;

    #[frame_support::runtime]
    mod runtime {
        #[runtime::runtime]
        #[runtime::derive(
            RuntimeCall,
            RuntimeEvent,
            RuntimeError,
            RuntimeOrigin,
            RuntimeFreezeReason,
            RuntimeHoldReason,
            RuntimeSlashReason,
            RuntimeLockId,
            RuntimeTask
        )]
        pub struct Test;

        #[runtime::pallet_index(0)]
        pub type System = frame_system::Pallet<Test>;

        // Other pallets...
    }

    pub fn new_test_ext() -> sp_io::TestExternalities {
        frame_system::GenesisConfig::<Test>::default()
            .build_storage()
            .unwrap()
            .into()
    }
}

You can also customize the genesis storage to set initial values for your runtime pallets. For example, you can set the initial balance for accounts like this:

// Build genesis storage according to the runtime's configuration
pub fn new_test_ext() -> sp_io::TestExternalities {
    // Define the initial balances for accounts
    let initial_balances: Vec<(AccountId32, u128)> = vec![
        (AccountId32::from([0u8; 32]), 1_000_000_000_000),
        (AccountId32::from([1u8; 32]), 2_000_000_000_000),
    ];

    let mut t = frame_system::GenesisConfig::<Test>::default()
        .build_storage()
        .unwrap();

    // Adding balances configuration to the genesis config
    pallet_balances::GenesisConfig::<Test> {
        balances: initial_balances,
    }
    .assimilate_storage(&mut t)
    .unwrap();

    t.into()
}

Note

For a more idiomatic approach, consult the Your first pallet guide from the Polkadot SDK rust documentation.

Pallet Configuration

Each pallet in the mocked runtime requires an associated configuration, specifying the types and values it depends on to function. These configurations often use basic or primitive types (e.g., u32, bool) instead of more complex types like structs or traits, ensuring the setup remains straightforward and manageable.

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
    ...
    type Index = u64;
    type BlockNumber = u64;
    type Hash = H256;
    type Hashing = BlakeTwo256;
    type AccountId = u64;
    ...
}

impl pallet_template::Config for Test {
    type RuntimeEvent = RuntimeEvent;
    type WeightInfo = ();
    ...
}

The configuration should be set for each pallet existing in the mocked runtime.

Note

The simplification of types is for simplifying the testing process. For example, AccountId is u64, meaning a valid account address can be an unsigned integer:

let alice_account: u64 = 1;

Where to Go Next

With the mock environment in place, developers can now test and explore how pallets interact and ensure they work seamlessly together. For further details about mocking runtimes, see the following Polkadot SDK docs guide.

  • Guide Pallet Testing


    Learn how to efficiently test pallets in the Polkadot SDK, ensuring your pallet operations are reliable and secure.

    Reference

Last update: December 13, 2024
| Created: December 13, 2024