Skip to main content

Testing Utilities

Cardano DevKit provides testing utilities for unit and integration testing of Cardano applications.

Overview

import {
createMockLucid,
createMockProvider,
mockTransaction,
mockUTxO,
generateTestSeed,
generateTestAddress,
assertTxSucceeds,
assertTxFails,
} from 'cardano-devkit/testing';

Mock Objects

createMockLucid(config?)

Create a mock Lucid instance for unit testing.

import { createMockLucid } from 'cardano-devkit/testing';

const mockLucid = createMockLucid({
network: 'Preprod',
balance: 100_000_000n, // 100 ADA
});

// Use in tests
const address = await mockLucid.wallet().address();
const utxos = await mockLucid.wallet().getUtxos();

createMockProvider(config?)

Create a mock provider for testing without network access.

import { createMockProvider } from 'cardano-devkit/testing';

const mockProvider = createMockProvider({
protocolParameters: {
minFeeA: 44,
minFeeB: 155381,
maxTxSize: 16384,
},
utxos: [
mockUTxO({ lovelace: 50_000_000n }),
mockUTxO({ lovelace: 30_000_000n }),
],
});

// Use with DevKit
const devKit = createDevKit({
network: 'Custom',
customProvider: mockProvider,
});

mockTransaction(config)

Create a mock transaction for testing.

import { mockTransaction } from 'cardano-devkit/testing';

const tx = mockTransaction({
inputs: [
{ txHash: 'abc123...', outputIndex: 0, lovelace: 10_000_000n }
],
outputs: [
{ address: 'addr_test1...', lovelace: 5_000_000n }
],
fee: 200_000n,
});

mockUTxO(config)

Create a mock UTxO for testing.

import { mockUTxO } from 'cardano-devkit/testing';

const utxo = mockUTxO({
txHash: 'abc123...',
outputIndex: 0,
address: 'addr_test1...',
lovelace: 10_000_000n,
assets: {
[policyId + assetName]: 100n,
},
datum: { owner: 'addr_test1...' },
});

Test Data Generators

generateTestSeed()

Generate a deterministic seed phrase for testing.

import { generateTestSeed } from 'cardano-devkit/testing';

// Different seed each time
const seed1 = generateTestSeed();

// Deterministic seed from index
const seed2 = generateTestSeed({ index: 0 }); // Always same seed
const seed3 = generateTestSeed({ index: 1 }); // Different but consistent

generateTestAddress(network)

Generate a test address.

import { generateTestAddress } from 'cardano-devkit/testing';

const testAddr = generateTestAddress('Preprod');
// addr_test1qz...

const mainnetAddr = generateTestAddress('Mainnet');
// addr1qy...

generateTestAsset(config?)

Generate test native asset data.

import { generateTestAsset } from 'cardano-devkit/testing';

const asset = generateTestAsset({
policyId: 'abc123...',
assetName: 'TestToken',
amount: 1000n,
});

Assertions

assertTxSucceeds(fn)

Assert that a transaction succeeds.

import { assertTxSucceeds } from 'cardano-devkit/testing';

await assertTxSucceeds(async () => {
const tx = await lucid.newTx()
.pay.ToAddress(recipient, { lovelace: 5_000_000n })
.complete();
return await tx.sign.withWallet().complete().then(s => s.submit());
});

assertTxFails(fn, expectedError?)

Assert that a transaction fails.

import { assertTxFails } from 'cardano-devkit/testing';

await assertTxFails(
async () => {
// This should fail - trying to send more than balance
const tx = await lucid.newTx()
.pay.ToAddress(recipient, { lovelace: 999_000_000_000n })
.complete();
},
'InsufficientFunds'
);

assertValidAddress(address)

Assert address is valid.

import { assertValidAddress } from 'cardano-devkit/testing';

assertValidAddress('addr_test1qz...'); // passes
assertValidAddress('invalid'); // throws

assertValidTxHash(hash)

Assert transaction hash is valid.

import { assertValidTxHash } from 'cardano-devkit/testing';

assertValidTxHash('abc123...'); // passes if valid 64-char hex

Test Fixtures

createTestFixtures(config)

Create a set of test fixtures.

import { createTestFixtures } from 'cardano-devkit/testing';

const fixtures = createTestFixtures({
network: 'Preprod',
walletCount: 3,
utxoPerWallet: 5,
adaPerUtxo: 10_000_000n,
});

// Access fixtures
const { wallets, utxos, addresses } = fixtures;

createScenario(name, setup)

Create a reusable test scenario.

import { createScenario } from 'cardano-devkit/testing';

const escrowScenario = createScenario('escrow', async (devKit) => {
// Setup escrow contract
const buyer = generateTestAddress('Preprod');
const seller = generateTestAddress('Preprod');
const arbiter = generateTestAddress('Preprod');

return { buyer, seller, arbiter };
});

// Use in tests
const { buyer, seller, arbiter } = await escrowScenario.setup(devKit);

Integration with Jest

Setup File

Create jest.setup.ts:

import { configureTestEnvironment } from 'cardano-devkit/testing';

beforeAll(async () => {
await configureTestEnvironment({
network: 'LocalDevnet',
startDevnet: true,
blockTime: 100,
});
});

afterAll(async () => {
await cleanupTestEnvironment();
});

Example Test

import { createDevKit } from 'cardano-devkit';
import {
createMockLucid,
generateTestSeed,
assertTxSucceeds,
} from 'cardano-devkit/testing';

describe('Payment', () => {
let devKit: ReturnType<typeof createDevKit>;

beforeEach(() => {
devKit = createDevKit({ network: 'LocalDevnet' });
});

test('sends ADA successfully', async () => {
const lucid = await devKit.init();
lucid.selectWallet.fromSeed(generateTestSeed());

await assertTxSucceeds(async () => {
const tx = await lucid.newTx()
.pay.ToAddress('addr_test1...', { lovelace: 5_000_000n })
.complete();
return await tx.sign.withWallet().complete().then(s => s.submit());
});
});

test('fails with insufficient funds', async () => {
const mockLucid = createMockLucid({ balance: 1_000_000n });

await assertTxFails(async () => {
const tx = await mockLucid.newTx()
.pay.ToAddress('addr_test1...', { lovelace: 5_000_000n })
.complete();
}, 'InsufficientFunds');
});
});

Property-Based Testing

withRandomData(generator, fn)

Run tests with random data.

import { withRandomData, generators } from 'cardano-devkit/testing';

test('validates any valid address', async () => {
await withRandomData(
generators.address('Preprod'),
async (address) => {
expect(validateAddress(address)).toBe(true);
},
{ iterations: 100 }
);
});

Generators

import { generators } from 'cardano-devkit/testing';

// Generate random valid data
const addr = generators.address('Preprod');
const hash = generators.txHash();
const lovelace = generators.lovelace({ min: 1_000_000n, max: 100_000_000n });
const seed = generators.seedPhrase();

Devnet Integration

withDevnet(fn)

Run test with temporary devnet.

import { withDevnet } from 'cardano-devkit/testing';

test('full integration test', async () => {
await withDevnet(async (devKit, faucet) => {
// Get funded wallet
const lucid = await devKit.init();
lucid.selectWallet.fromSeed(generateTestSeed());
const address = await lucid.wallet().address();

// Fund from faucet
await faucet.fundAddress(address, 100_000_000n);

// Run test
const balance = await lucid.wallet().getBalance();
expect(balance.lovelace).toBe(100_000_000n);
}, { blockTime: 100 });
});

Snapshot Testing

snapshotTransaction(tx)

Create a snapshot of transaction for comparison.

import { snapshotTransaction } from 'cardano-devkit/testing';

test('transaction structure', async () => {
const tx = await lucid.newTx()
.pay.ToAddress(recipient, { lovelace: 5_000_000n })
.complete();

expect(snapshotTransaction(tx)).toMatchSnapshot();
});

TypeScript Types

import type {
MockLucidConfig,
MockProviderConfig,
MockTransactionConfig,
MockUTxOConfig,
TestFixtures,
TestScenario,
Generator,
} from 'cardano-devkit/testing';

Best Practices

1. Use Mocks for Unit Tests

// Unit test - no network needed
const mockLucid = createMockLucid({ balance: 100_000_000n });

2. Use Devnet for Integration Tests

// Integration test - real blockchain
await withDevnet(async (devKit) => {
// Full end-to-end test
});

3. Deterministic Seeds

// Use indexed seeds for reproducibility
const aliceSeed = generateTestSeed({ index: 0 });
const bobSeed = generateTestSeed({ index: 1 });

4. Clean Up After Tests

afterEach(async () => {
await cleanupTestState();
});

5. Test Error Cases

test('handles errors gracefully', async () => {
await assertTxFails(async () => {
// Trigger error condition
}, 'ExpectedErrorType');
});