Skip to content

E2E Testing with Moonwall

Introduction

Moonwall is an end-to-end testing framework designed explicitly for Polkadot SDK-based blockchain networks. It addresses one of the most significant challenges in blockchain development: managing complex test environments and network configurations.

Moonwall consolidates this complexity by providing the following:

  • A centralized configuration management system that explicitly defines all network parameters
  • A standardized approach to environment setup across different Substrate-based chains
  • Built-in utilities for common testing scenarios and network interactions

Developers can focus on writing meaningful tests rather than managing infrastructure complexities or searching through documentation for configuration options.

Prerequisites

Before you begin, ensure you have the following installed:

Install Moonwall

Moonwall can be installed globally for system-wide access or locally within specific projects. This section covers both installation methods.

Tip

This documentation corresponds to Moonwall version 5.9.1. To avoid compatibility issues with the documented features, ensure you're using the matching version.

Global Installation

Global installation provides system-wide access to the Moonwall CLI, making it ideal for developers working across multiple blockchain projects. Install it by running one of the following commands:

npm install -g @moonwall/cli@5.9.1
pnpm -g install @moonwall/cli@5.9.1
yarn global add @moonwall/cli@5.9.1

Now, you can run the moonwall command from your terminal.

Local Installation

Local installation is recommended for better dependency management and version control within a specific project. First, initialize your project:

mkdir my-moonwall-project
cd my-moonwall-project
npm init -y

Then, install it as a local dependency:

npm install @moonwall/cli@5.9.1
pnpm install @moonwall/cli@5.9.1
yarn add @moonwall/cli@5.9.1

Initialize Moonwall

The moonwall init command launches an interactive wizard to create your configuration file:

moonwall init

During setup, you will see prompts for the following parameters:

  • label - identifies your test configuration
  • global timeout - maximum time (ms) for test execution
  • environment name - name for your testing environment
  • network foundation - type of blockchain environment to use
  • tests directory - location of your test files

Select Enter to accept defaults or input custom values. You should see something like this:

moonwall init ✔ Provide a label for the config file moonwall_config ✔ Provide a global timeout value 30000 ✔ Provide a name for this environment default_env ✔ What type of network foundation is this? dev ✔ Provide the path for where tests for this environment are kept tests/ ? Would you like to generate this config? (no to restart from beginning) (Y/n)

The wizard generates a moonwall.config file:

{
    "label": "moonwall_config",
    "defaultTestTimeout": 30000,
    "environments": [
        {
            "name": "default_env",
            "testFileDir": ["tests/"],
            "foundation": {
                "type": "dev"
            }
        }
    ]
}

The default configuration requires specific details about your blockchain node and test requirements:

  • The foundation object defines how your test blockchain node will be launched and managed. The dev foundation, which runs a local node binary, is used for local development

For more information about available options, check the Foundations section.

  • The connections array specifies how your tests will interact with the blockchain node. This typically includes provider configuration and endpoint details

A provider is a tool that allows you or your application to connect to a blockchain network and simplifies the low-level details of the process. A provider handles submitting transactions, reading state, and more. For more information on available providers, check the Providers supported page in the Moonwall documentation.

Here's a complete configuration example for testing a local node using Polkadot.js as a provider:

{
    "label": "moonwall_config",
    "defaultTestTimeout": 30000,
    "environments": [
        {
            "name": "default_env",
            "testFileDir": ["tests/"],
            "foundation": {
                "launchSpec": [
                    {
                        "binPath": "./node-template",
                        "newRpcBehaviour": true,
                        "ports": { "rpcPort": 9944 }
                    }
                ],
                "type": "dev"
            },
            "connections": [
                {
                    "name": "myconnection",
                    "type": "polkadotJs",
                    "endpoints": ["ws://127.0.0.1:9944"]
                }
            ]
        }
    ]
}

Writing Tests

Moonwall uses the describeSuite function to define test suites, like using Mocha. Each test suite requires the following:

  • id - unique identifier for the suite
  • title - descriptive name for the suite
  • foundationMethods - specifies the testing environment (e.g., dev for local node testing)
  • testCases - a callback function that houses the individual test cases of this suite

The following example shows how to test a balance transfer between two accounts:

import '@polkadot/api-augment';
import { describeSuite, expect } from '@moonwall/cli';
import { Keyring } from '@polkadot/api';

describeSuite({
  id: 'D1',
  title: 'Demo suite',
  foundationMethods: 'dev',
  testCases: ({ it, context, log }) => {
    it({
      id: 'T1',
      title: 'Test Case',
      test: async () => {
        // Set up polkadot.js API and testing accounts
        let api = context.polkadotJs();
        let alice = new Keyring({ type: 'sr25519' }).addFromUri('//Alice');
        let charlie = new Keyring({ type: 'sr25519' }).addFromUri('//Charlie');

        // Query Charlie's account balance before transfer
        const balanceBefore = (await api.query.system.account(charlie.address))
          .data.free;

        // Before transfer, Charlie's account balance should be 0
        expect(balanceBefore.toString()).toEqual('0');
        log('Balance before: ' + balanceBefore.toString());

        // Transfer from Alice to Charlie
        const amount = 1000000000000000;
        await api.tx.balances
          .transferAllowDeath(charlie.address, amount)
          .signAndSend(alice);

        // Wait for the transaction to be included in a block.
        // This is necessary because the balance is not updated immediately.
        // Block time is 6 seconds.
        await new Promise((resolve) => setTimeout(resolve, 6000));

        // Query Charlie's account balance after transfer
        const balanceAfter = (await api.query.system.account(charlie.address))
          .data.free;

        // After transfer, Charlie's account balance should be 1000000000000000
        expect(balanceAfter.toString()).toEqual(amount.toString());
        log('Balance after: ' + balanceAfter.toString());
      },
    });
  },
});

This test demonstrates several key concepts:

  • Initializing the Polkadot.js API through Moonwall's context and setting up test accounts
  • Querying on-chain state
  • Executing transactions
  • Waiting for block inclusion
  • Verifying results using assertions

Running the Tests

Execute your tests using the test Moonwall CLI command. For the default environment setup run:

moonwall test default_env -c moonwall.config

The test runner will output detailed results showing:

  • Test suite execution status
  • Individual test case results
  • Execution time
  • Detailed logs and error messages (if any)

Example output:

moonwall test default_env -c moonwall.config stdout | tests/test1.ts > 🗃️ D1 Demo suite > 📁 D1T1 Test Case 2025-01-21T19:27:55.624Z test:default_env Balance before: 0 stdout | tests/test1.ts > 🗃️ D1 Demo suite > 📁 D1T1 Test Case 2025-01-21T19:28:01.637Z test:default_env Balance after: 1000000000000000 ✓ default_env tests/test1.ts (1 test) 6443ms ✓ 🗃️ D1 Demo suite > 📁 D1T1 Test Case 6028ms Test Files 1 passed (1) Tests 1 passed (1) Start at 16:27:53 Duration 7.95s (transform 72ms, setup 0ms, collect 1.31s, tests 6.44s, environment 0ms, prepare 46ms) ✅ All tests passed

Where to Go Next

For a comprehensive guide to Moonwall's full capabilities, available configurations, and advanced usage, see the official Moonwall documentation.

Last update: January 28, 2025
| Created: January 28, 2025