Skip to Content
DevelopersNTSTesting

Testing

️⚠️
NTS is in active development and currently unstable. It is open to select early developers for exploration but is not yet suitable for production use. Follow developer updates for the latest changes, as performance is not at production level.

The testing framework is currently still in development

Testing N1 Applications with Jest

This guide explains how to write effective tests for your N1 applications using Jest and the N1 testing utilities.

Overview

The N1 framework provides testing utilities that allow you to simulate the execution of your N1 applications in a controlled environment. These utilities, combined with Jest, enable you to verify that your application behaves as expected without deploying it to the blockchain.

Setup

To test your N1 applications, you’ll need to install the following dependencies:

npm install --save-dev jest @n1xyz/nts-compiler

Add the following to your package.json:

"scripts": { "test": "jest" }

Basic Test Structure

Here’s the basic structure of a test file for an N1 application:

import { compileCodeAndIdl, MockDB, MockNAppClient } from "@n1xyz/nts-compiler"; describe('YourApp Tests', () => { // Your application code (or import it) const testCode = ` import { NApp, NMap, createExecutableFunctions } from '@n1xyz/nts-compiler'; // Your app code here... `; let client: any; const testSigner = 'test_signer'; const testAdmin = 'test_admin'; const testAppId = 'test_app'; beforeAll(async () => { let mockDB = new MockDB(); const { code, idl } = compileCodeAndIdl(testCode); client = MockNAppClient.loadClientFromCodeAndIDL(code, idl, { signer: testSigner, appAdmin: testAdmin, appId: testAppId, db: mockDB, }); }); test('should do something', async () => { // Your test here }); });

Testing Function Execution

To test the execution of a function in your N1 application:

test('should successfully set a message', async () => { const testMessage = 'Hello, World!'; const result = await client.executeAction('setMessage', [testMessage]); // Verify the write operation const messageWrite = result.transactionWrites.find((write: any) => write.fieldId === `messages____${testSigner}` ); expect(messageWrite).toBeTruthy(); expect(messageWrite.value).toBe(testMessage); // Verify we can read the message from the database const storedMessage = await client.readField(`messages____${testSigner}`); expect(storedMessage).toBe(testMessage); });

Testing Validation Rules

To test validation rules and error handling:

test('should fail when setting an empty message', async () => { // Test with empty string await expect( client.executeAction('setMessage', ['']) ).rejects.toThrow('Message cannot be empty'); // Test with whitespace only await expect( client.executeAction('setMessage', [' ']) ).rejects.toThrow('Message cannot be empty'); });

Testing State Updates

To test that your application correctly updates its state:

test('should update existing message', async () => { const initialMessage = 'Initial message'; const updatedMessage = 'Updated message'; // Set initial message await client.executeAction('setMessage', [initialMessage]); // Update the message const result = await client.executeAction('setMessage', [updatedMessage]); // Verify the write operation const messageWrite = result.transactionWrites.find((write: any) => write.fieldId === `messages____${testSigner}` ); expect(messageWrite).toBeTruthy(); expect(messageWrite.value).toBe(updatedMessage); // Verify the updated message in the database const storedMessage = await client.readField(`messages____${testSigner}`); expect(storedMessage).toBe(updatedMessage); });

Testing Token Operations

For applications that involve token operations like minting:

test('should successfully create a token', async () => { const testName = 'Hello, World!'; const testSymbol = 'HW'; const testSupply = 1000; const result = await client.executeAction('createMint', [testName, testSymbol, testSupply]); // Verify the write operation const mintWrite = result.transactionWrites.find((write: any) => write.fieldId === `mints____${testSymbol}` ); expect(mintWrite).toBeTruthy(); expect(JSON.parse(mintWrite.value).meta.name).toBe(testName); // Verify we can read the message from the database const storedMint = JSON.parse(await client.readField(`mints____${testSymbol}`)); expect(storedMint.meta.name).toBe(testName); expect(storedMint.meta.symbol).toBe(testSymbol); expect(storedMint.amount).toBe(testSupply); // Verify balance const balance = Number(await client.readField(`user_balance_${storedMint.mint}_${testSigner}`)); expect(balance).toBe(testSupply); });

Best Practices

  1. Test Isolation: Each test should be independent and not rely on the state from previous tests.

  2. Test Edge Cases: Include tests for edge cases and error conditions.

  3. Clear Test Names: Use descriptive test names that explain what is being tested.

  4. Arrange-Act-Assert: Structure your tests with a clear setup, action, and verification phases.

  5. Test Data Management: Use constant values for test data to make tests more predictable.

API Reference

MockDB

A mock database that simulates the blockchain’s storage.

const mockDB = new MockDB();

compileCodeAndIdl

Compiles your N1 application code and generates the IDL (Interface Definition Language).

const { code, idl } = compileCodeAndIdl(yourAppCode);

MockNAppClient

Creates a client that can interact with your mock N1 application.

const client = MockNAppClient.loadClientFromCodeAndIDL(code, idl, { signer: testSigner, appAdmin: testAdmin, appId: testAppId, db: mockDB, });

client.executeAction

Executes an action (function) in your application.

const result = await client.executeAction('functionName', [param1, param2]);

client.readField

Reads a field from the mock database.

const value = await client.readField('fieldId');

result.transactionWrites

An array of write operations performed during function execution.

const write = result.transactionWrites.find(w => w.fieldId === 'yourFieldId');
Last updated on