Skip to main content

Writing Tests Examples

This page demonstrates how to write test for FunC contracts created in with Blueprint SDK (Sandbox). Test suites built for demo contract fireworks. The fireworks is a smart contract which initially run via set_first message.

Once a new FunC project is created via npm create ton@latest, a test file tests/contract.spec.ts will be autogenerated in the project directory for testing the contract:

import ...

describe('Fireworks', () => {
...


expect(deployResult.transactions).toHaveTransaction({
...
});

});

it('should deploy', async () => {
// the check is done inside beforeEach
// blockchain and fireworks are ready to use
});

Running tests using the following command:

npx blueprint test

Additional options and vmLogs may be specified with blockchain.verbosity:

blockchain.verbosity = {
...blockchain.verbosity,
blockchainLogs: true,
vmLogs: 'vm_logs_full',
debugLogs: true,
print: false,
}

Direct Unit Tests

Fireworks demonstrate different operating with sending messages in the TON Blockchain.

Once you deploy this with message set_first with enough TON amount, it will be automatically executed with primary and usable combinations of send modes.

Fireworks redeployed itself, as result it will be created 3 entities of Fireworks entities, while each of entity has own ID(keep it in storage) and, as a result, different Smart Contract Address.

For clearness define different by ID Fireworks instances (different state_init) with the following names:

  • 1 - Fireworks setter - The entity that spread different launch op codes. Could be extended up to four different opcodes.
  • 2 - Fireworks launcher-1 - The Fireworks instance, which launch first fireworks, means messages will be sent to the launcher.
  • 3 - Fireworks launcher-2 - The Fireworks instance, which launch second fireworks, means messages will be sent launcher.
Expand details on transactions

index - is an ID of a transaction in the launchResult array.

  • 0 - External request to the treasury (the Launcher) that resulted with a outbound message op::set_first with 2.5 to fireworks
  • 1 - The transaction in Fireworks setter contract invoked with op::set_first and executed with two outbound messages to the Fireworks Launcher-1 and Fireworks Launcher-2
  • 2 - The transaction in the Fireworks launcher 1 invoked with op::launch_first, and executed with four outbound messages to the Launcher.
  • 3 - The transaction in the Fireworks launcher 2 invoked with op::launch_second, and executed with a outbound message to the Launcher.
  • 4 - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 0.
  • 5 - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 1
  • 6 - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 2
  • 7 - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 128 + 32
  • 8 - Transaction in the Launcher with incoming message from the Fireworks launcher 2. This message sent with send mode = 64

Each 'firework' - is outbound message with a unique message body appears in transactions with ID:3 and ID:4.

Bellow the list of test for each transaction expected successfully executed. Transaction[ID:0] External request to the treasury (the Launcher) that resulted with a outbound message op::set_first with 2.5 to fireworks. In case you will deploy Fireworks to the blockchain launcher is your wallet.

Transaction ID:1 Success Test

This test checks if the fireworks are successfully set by sending a transaction with a value of 2.5 TON. This is the simplest case, the main purpose here to assert result of transaction success property to true.

To filter certain transaction from the launhcResult.transactions array, we can use the most convince fields. With from (contract sender address), to (contract destination address), op (Op code value) - we will retrieve only one transaction for this combination.

The transaction[ID:1] in Fireworks Setter contract invoked with op::set_first and executed with two outbound messages to the Fireworks launcher-1 and Fireworks launcher-2


it('first transaction[ID:1] should set fireworks successfully', async () => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5'));


expect(launchResult.transactions).toHaveTransaction({
from: launcher.address,
to: fireworks.address,
success: true,
op: Opcodes.set_first
})

});

Transaction ID:2 Success Test

This test checks if the transaction[ID:2] executed successfully.

The transaction in the Fireworks launcher 1 invoked with op::launch_first, and executed with four outbound messages to the Launcher.

    it('should exist a transaction[ID:2] which launch first fireworks successfully', async () => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5'));

expect(launchResult.transactions).toHaveTransaction({
from: fireworks.address,
to: launched_f1.address,
success: true,
op: Opcodes.launch_first,
outMessagesCount: 4,
destroyed: true,
endStatus: "non-existing",
})

printTransactionFees(launchResult.transactions);

});

In cases, when transaction should affect the state of contract, it is possible to specify this with destroyed, endStatus fields.

The full list of Account Status related fields:

  • destroyed - true - if the existing contract was destroyed due to executing a certain transaction. Otherwise - false.
  • deploy - Custom Sandbox flag that indicates whether the contract was deployed during this transaction. true if contract before this transaction was not initialized and after this transaction became initialized. Otherwise - false.
  • oldStatus - AccountStatus before transaction execution. Values: 'uninitialized', 'frozen', 'active', 'non-existing'.
  • endStatus - AccountStatus after transaction execution. Values: 'uninitialized', 'frozen', 'active', 'non-existing'.

Transaction ID:3 Success Test

This test checks if the transaction[ID:3] executed successfully.

The transaction[ID:3] carries out in the Fireworks launcher 1, invokes with op::launch_first, and executes with four outbound messages to the Launcher.


it('should exist a transaction[ID:3] which launch second fireworks successfully', async () => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5'));

expect(launchResult.transactions).toHaveTransaction({
from: fireworks.address,
to: launched_f2.address,
success: true,
op: Opcodes.launch_second,
outMessagesCount: 1
})

printTransactionFees(launchResult.transactions);

});




Transaction ID:4 Success Test

This test checks if the transaction[ID:4] executed successfully.

Transaction[ID:4] carries out in the Launcher(Deploy Wallet) with incoming message from the Fireworks launcher 1. This message sent with send mode = 0 in the Transaction[ID:2].

 it('should exist a transaction[ID:4] with a comment send mode = 0', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

expect(launchResult.transactions).toHaveTransaction({
from: launched_f1.address,
to: launcher.address,
success: true,
body: beginCell().storeUint(0,32).storeStringTail("send mode = 0").endCell() // 0x00000000 comment opcode and encoded comment

});
})

Transaction ID:5 Success Test

This test checks if the transaction[ID:5] executed successfully.

Transaction[ID:5] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 1

     it('should exist a transaction[ID:5] with a comment send mode = 1', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

expect(launchResult.transactions).toHaveTransaction({
from: launched_f1.address,
to: launcher.address,
success: true,
body: beginCell().storeUint(0,32).storeStringTail("send mode = 1").endCell() // 0x00000000 comment opcode and encoded comment
});

})


Transaction ID:6 Success Test

This test checks if the transaction[ID:6] executed successfully.

The transaction[ID:6] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 2

    it('should exist a transaction[ID:6] with a comment send mode = 2', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

expect(launchResult.transactions).toHaveTransaction({
from: launched_f1.address,
to: launcher.address,
success: true,
body: beginCell().storeUint(0,32).storeStringTail("send mode = 2").endCell() // 0x00000000 comment opcode and encoded comment
});

})

Transaction ID:7 Success Test

This test checks if the transaction[ID:7] executed successfully.

The transaction[ID:7] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with send mode = 128 + 32

     it('should exist a transaction[ID:7] with a comment send mode = 32 + 128', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

expect(launchResult.transactions).toHaveTransaction({
from: launched_f1.address,
to: launcher.address,
success: true,
body: beginCell().storeUint(0,32).storeStringTail("send mode = 32 + 128").endCell() // 0x00000000 comment opcode and encoded comment
});
})

Transaction ID:8 Success Test

This test checks if the transaction[ID:8] executed successfully.

The transaction[ID:8] carries out in the Launcher with incoming message from the Fireworks launcher 2. This message sent with send mode = 64

  it('should exist a transaction[ID:8] with a comment send mode = 64', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

expect(launchResult.transactions).toHaveTransaction({
from: launched_f2.address,
to: launcher.address,
success: true,
body: beginCell().storeUint(0,32).storeStringTail("send_mode = 64").endCell() // 0x00000000 comment opcode and encoded comment

});

})

Printing and Reading Transaction Fees

During the test, reading the details about fees can be useful for optimizing the contract. The printTransactionFees function prints the entire transaction chain in a convenient manner."


it('should be executed and print fees', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

console.log(printTransactionFees(launchResult.transactions));

});

For instance, in case of launchResult the following table will be printed:

(index)opvalueInvalueOuttotalFeesoutActions
0'N/A''N/A''2.5 TON''0.010605 TON'1
1'0x5720cfeb''2.5 TON''2.185812 TON''0.015836 TON'2
2'0x6efe144b''1.092906 TON''1.081142 TON''0.009098 TON'4
3'0xa2e2c2dc''1.092906 TON''1.088638 TON''0.003602 TON'1
4'0x0''0.099 TON''0 TON''0.000309 TON'0
5'0x0''0.1 TON''0 TON''0.000309 TON'0
6'0x0''0.099 TON''0 TON''0.000309 TON'0
7'0x0''0.783142 TON''0 TON''0.000309 TON'0
8'0x0''1.088638 TON''0 TON''0.000309 TON'0

index - is an ID of a transaction in the launchResult array.

  • 0 - External request to the treasury (the Launcher) that resulted in a message op::set_first to Fireworks
  • 1 - The Fireworks transaction that resulted in 4 messages to the Launcher
  • 2 - Transaction on Launched Fireworks - 1 from the Launcher, message sent with op::launch_first op code.
  • 2 - Transaction on Launched Fireworks - 2 from the Launcher, message sent with op::launch_second op code.
  • 4 - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with send mode = 0
  • 5 - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with send mode = 1
  • 6 - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with send mode = 2
  • 7 - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with send mode = 128 + 32
  • 8 - Transaction on Launcher with incoming message from the Launched Fireworks - 2, message sent with send mode = 64

Transaction Fees Tests

This test verifies whether the transaction fees for launching the fireworks are as expected. It is possible to define custom assertions for different parts of commission fees.


it('should be executed with expected fees', async() => {

const launcher = await blockchain.treasury('launcher');

const launchResult = await fireworks.sendDeployLaunch(
launcher.getSender(),
toNano('2.5'),
);

//totalFee
console.log('total fees = ', launchResult.transactions[1].totalFees);

const tx1 = launchResult.transactions[1];
if (tx1.description.type !== 'generic') {
throw new Error('Generic transaction expected');
}

//computeFee
const computeFee = tx1.description.computePhase.type === 'vm' ? tx1.description.computePhase.gasFees : undefined;
console.log('computeFee = ', computeFee);

//actionFee
const actionFee = tx1.description.actionPhase?.totalActionFees;
console.log('actionFee = ', actionFee);


if ((computeFee == null || undefined) ||
(actionFee == null || undefined)) {
throw new Error('undefined fees');
}

//The check, if Compute Phase and Action Phase fees exceed 1 TON
expect(computeFee + actionFee).toBeLessThan(toNano('1'));


});