Chromia
Start Building

01 / 01Guide

Running integration tests with Rell and TypeScript

Integration tests confirm that your frontend and backend work together correctly. This guide shows you how to build a TypeScript test harness for your Rell code, start a Rell test node, and run queries and transactions against it.

Verify your environment

Before you start, confirm these prerequisites:

  • Docker: Docker is running, and your user has permission to start containers.
  • Node.js & npm: Both are installed and configured correctly.
  • TypeScript: TypeScript is installed, either globally or as a project dependency.

Check your project setup

Confirm that your project is laid out correctly:

  • Project structure: src/main.rell and src/main.test.ts are in the right locations.
  • Package.json: Your package.json contains the required scripts and dependencies.

Project setup

Set up the project with the following steps:

bash
chr create-rell-dapp rell-it
bash
cd rell-it
bash
npm init --yes
bash
npm install postchain-client
bash
npm install typescript jest @types/jest ts-jest testcontainers @testcontainers/postgresql --save-dev
bash
npx tsc --init
bash
npx ts-jest config:init

The harness relies on the Jest test framework and Testcontainers. To talk to Rell, it uses postchain-client.

Integration test with Jest and Testcontainers

Define your integration tests in a new file, src/main.test.ts:

src/main.test.ts
import { cwd } from "process";import { IClient, createClient } from "postchain-client";import { GenericContainer, Network, StartedNetwork, StartedTestContainer, Wait } from "testcontainers";import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql";// Define the test suitedescribe("Rell Integration Tests", () => {  let network: StartedNetwork;  let postgres: StartedPostgreSqlContainer;  let container: StartedTestContainer;  let client: IClient;  // Set up PostgreSQL container and Chromia node container  beforeAll(async () => {    // Start a new network for containers    network = await new Network().start();    // Start a PostgreSQL container    postgres = await new PostgreSqlContainer("postgres:16.3-alpine3.20")      .withNetwork(network)      .withExposedPorts(5432)      .withDatabase("postchain")      .withPassword("postchain")      .withUsername("postchain")      .withNetworkAliases("postgres")      .start();    // Start a Chromia node container    container = await new GenericContainer("registry.gitlab.com/chromaway/core-tools/chromia-cli/chr:latest")      .withNetwork(network)      .withCopyDirectoriesToContainer([{ source: cwd(), target: "/usr/app" }])      .withExposedPorts(7740)      .withEnvironment({        CHR_DB_URL: "jdbc:postgresql://postgres/postchain",      })      .withCommand(["chr", "node", "start", "--wipe"])      .withWaitStrategy(Wait.forLogMessage("Blockchain has been started"))      .withStartupTimeout(60000)      .start();    // Create a Postchain client for communication    client = await createClient({      blockchainIid: 0, // Instance Identifier (Iid) of the targeted blockchain      nodeUrlPool: "http://localhost:" + container.getMappedPort(7740),      merkleHashVersion: 1    });  }, 30000);  // Stop containers after all tests are complete  afterAll(async () => {    await container.stop();    await postgres.stop();    await network.stop();  });  // Integration tests  it("Can update and query dapp", async () => {    // Verify initial query result    expect(await client.query("hello_world")).toBe("Hello World!");    // Send a transaction to update the dapp    expect((await client.sendTransaction({ name: "set_name", args: ["Joe"] })).statusCode).toBe(200);    // Verify the updated query result    expect(await client.query("hello_world")).toBe("Hello Joe!");  }, 60000);});

Explanation

  • Docker network: The test creates a dedicated network so the PostgreSQL and Chromia node containers can reach each other.
  • PostgreSQL container: Provides the database for the Rell application and starts with default credentials.
  • Chromia node container: Uses the Chromia CLI to start a test node. The project files are copied in, and the node is configured with the database URL. Running chr node start --wipe initializes the test node.
  • Client configuration: postchain-client connects to the Chromia node through the mapped port.

The test startup ends with this step:

typescript
client = await createClient({  blockchainIid: 0,  nodeUrlPool: "http://localhost:" + container.getMappedPort(7740),});

Here, nodeUrlPool points at the node's port 7740 inside the container; Docker maps it to a random port on your machine, and getMappedPort returns that host port.

Next, add the tests themselves:

typescript
// Perform integration testsit("Can update and query dapp", async () => {  // Verify initial query result  expect(await client.query("hello_world")).toBe("Hello World!");  // Send a transaction to update the dapp  expect((await client.sendTransaction({ name: "set_name", args: ["Joe"] })).statusCode).toBe(200);  // Verify the updated query result  expect(await client.query("hello_world")).toBe("Hello Joe!");});

The test sends a query and a transaction to the node, then checks the results.

Test and debug your setup

  • Run tests: Execute npx jest. Jest must be installed and configured.

  • Test output: Look through the output for errors or failures, and confirm your Rell queries and operations are correct.

  • Debugging:

    • Container logs: Inspect the Postgres and Chromia container logs for errors.

    • Network configuration: Confirm the containers share a network and can reach one another.

    • Database initialization: Confirm the database starts with the schema and data your tests expect.

Run the tests

Run the tests with Jest:

bash
npx jest
result
 PASS  src/main.test.ts (11.855 s)  Rell integration tests    ✓ Can update and query dapp (1659 ms)Test Suites: 1 passed, 1 totalTests:       1 passed, 1 totalSnapshots:   0 totalTime:        11.906 sRan all test suites.

This runs the integration tests, which call the Rell node and check the outcome of each query and transaction.

Continuous Integration (CI)

For Testcontainers to work in CI, Docker must be set up correctly in your pipeline:

yaml
# DinD service is required for Testcontainersservices:  - name: docker:dind    # Explicitly disable TLS to avoid Docker startup interruptions    command: ["--tls=false"]variables:  # Instruct Testcontainers to use the daemon of DinD. Use port 2375 for non-TLS connections.  DOCKER_HOST: "tcp://docker:2375"  # Instruct Docker not to start over TLS.  DOCKER_TLS_CERTDIR: ""  # Improve performance with overlayfs.  DOCKER_DRIVER: overlay2

Troubleshooting tips

  • Container startup: When containers won't start, check that the images exist and are current.
  • Network issues: Make sure Docker's network mode lets the containers communicate.
  • Timeouts: If tests time out, raise the timeout value or make sure the Chromia node starts fast enough.
Written for this site from Chromia’s public course. Code samples are the course’s own.