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');
});