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.rellandsrc/main.test.tsare in the right locations. - Package.json: Your
package.jsoncontains the required scripts and dependencies.
Project setup
Set up the project with the following steps:
chr create-rell-dapp rell-itcd rell-itnpm init --yesnpm install postchain-clientnpm install typescript jest @types/jest ts-jest testcontainers @testcontainers/postgresql --save-devnpx tsc --initnpx ts-jest config:initThe 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:
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 --wipeinitializes the test node. - Client configuration:
postchain-clientconnects to the Chromia node through the mapped port.
The test startup ends with this step:
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:
// 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:
npx jest 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:
# 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: overlay2image: node:16 # Specify a Node.js imagepipelines: default: - step: name: Run Tests script: - export TESTCONTAINERS_RYUK_DISABLED=true # Disable Ryuk if necessary - npx jest services: - dockerdefinitions: services: docker: memory: 2048 # Allocate memory for the Docker serviceTroubleshooting 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.