Skip to main content

Run Integration tests with Rell and TypeScript

Integration testing is crucial for ensuring that the frontend works seamlessly with the backend. In this guide, we'll set up a test harness in TypeScript to perform integration tests on our Rell code. We'll also start a Rell test node and perform queries and transactions.

Verify Your Environment

Ensure you have the following prerequisites set up:

  • Docker: Docker should be running, and you should have the necessary permissions to start containers.
  • Node.js & npm: Verify that Node.js and npm are installed and configured correctly.
  • TypeScript: Ensure TypeScript is installed globally or as part of your project dependencies.

Check Your Project Setup

Make sure your project structure is correct:

  • Project Structure: Ensure src/main.rell and src/main.test.ts are correctly placed.
  • Package.json: Verify that your package.json includes the necessary scripts and dependencies.

Project setup

Let's set up the project:

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

We will use the Jest test framework and Testcontainers for the test harness and postchain-client to communicate with Rell.

Integration test with Jest and Testcontainers

Create the test file src/main.test.ts to define your integration tests:

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 suite
describe("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:14.9-alpine3.18")
.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),
});
}, 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!");
});
});

Explanation

  • Docker network: A network is created to connect the PostgreSQL and Chromia node containers for seamless communication.
  • PostgreSQL container: This container hosts the database for the Rell application, initialized with default credentials.
  • Chromia node container: The Chromia CLI is used to start a test node, copy project files, and configure it with the database URL. The command chr node start --wipe initializes the test node.
  • Client configuration: The postchain-client is set up to communicate with the Chromia node using the mapped port.
note

Initializing containers in beforeAll is generally best. However, if restarting the Chromia node for each test case is necessary, use beforeEach. The PostgreSQL container can persist between tests, so beforeAll is sufficient.

We finalize the test startup with:

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

This configures the nodeUrlPool to point to port 7740, which is mapped internally to some other port and ensured by the getMappedPort call.

We can now add the actual tests as follows:

// Perform 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!");
});

This test does a query and a transaction towards the node and verifies the results.

Test and debug your setup

  • Run tests: Use the command npx jest to run your tests. Ensure Jest is properly installed and configured.

  • Test output: Examine the test output for any errors or failures. Ensure your Rell queries and operations are correct.

  • Debugging:

    • Container logs: Check the logs of your Postgres and Chromia containers for any errors.

    • Network configuration: Ensure the containers are correctly networked. They should be able to communicate with each other.

    • Database initialization: Ensure your database is initialized with the correct schema and data for testing.

Run the tests

You can run the tests using Jest:

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 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.906 s
Ran all test suites.

This command will execute integration tests by interacting with the Rell node and verifying queries and transactions.

Continuous Integration (CI)

Ensure Docker is configured correctly for CI environments to allow Testcontainers to run:

# DinD service is required for Testcontainers
services:
- 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: If containers fail to start, verify that the images are available and up-to-date.
  • Network issues: Ensure Docker's network mode allows containers to communicate properly.
  • Timeouts: If tests timeout, consider increasing the timeout value or ensuring that the Chromia node starts quickly enough.