Test Generation
This document provides details about the CLI command for generating tests using TSA. The tool analyzes failing executions (such as TVM exceptions or user-defined errors) and automatically generates test cases for them.
Overview
The tool generates tests that use the Sandbox framework to emulate the blockchain environment. These tests replicate specific failing executions by initializing the contract in the required state and sending internal messages that trigger the failure.
Additionally, the test contract wrapper is generated.
Note: Test generation is currently supported only for recv_internal and recv_external.
Command Example
java -jar tsa-cli.jar test-gen \
-p path/to/project \
--contract FunC contracts/contract.func \
--fift-std path/to/fiftstdlibOutput
After executing the command, a test file will be generated in the tests directory of the specified project.
The test filename is derived from the contract filename.
For example, jetton-minter.fc will result in JettonMinter.spec.ts.
Running the Generated Tests
You can run the generated tests using one of the following commands, depending on your project’s configuration:
yarn jest tests/TestFile.spec.fcnpm test tests/TestFile.spec.fcExample
For the following sample contract:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "stdlib.func";
(slice, int) safe_load(slice sl, int size) {
if (sl.slice_bits() < size) {
return (sl, 0);
}
return sl.load_uint(size);
}
int load_data() {
slice ds = get_data().begin_parse();
return ds~safe_load(8);
}
() recv_internal(slice in_msg_body) impure {
int op = in_msg_body~safe_load(32);
if (op == 0xffffffff) {
int stored_data = load_data();
if (stored_data == 255) {
throw(333);
}
}
return ();
}
The tool generates the following tests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ...
describe('tsa-tests-recv-internal', () => {
it('user-defined-error-333-0', async () => {
blockchain.now = 1712318909
const data: Cell = beginCell().storeUint(BigInt("0b11111111"), 8).endCell()
const contractAddr: Address = Address.parse("0:0000000000000000000000000000000000000000000000000000000000000000")
const contractBalance: bigint = 10000000n
const contract: SandboxContract<JettonMinter> = blockchain.openContract(new JettonMinter(contractAddr, code, data))
await contract.initializeContract(blockchain, contractBalance)
const srcAddr: Address = Address.parse("0:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
const msgBody: Cell = beginCell().storeUint(BigInt("0b11111111111111111111111111111111"), 32).endCell()
const msgCurrency: bigint = 10000000n
const bounce: boolean = false
const bounced: boolean = false
const sendMessageResult: SendMessageResult = await contract.internal(blockchain, srcAddr, msgBody, msgCurrency, bounce, bounced)
expect(sendMessageResult.transactions).toHaveTransaction({
from: srcAddr,
to: contractAddr,
exitCode: 333,
})
})
})
// ...
In this test, the contract is deployed via the sandbox with a contract data containing a number 255 and
the recv_internal function is called with a message body containing the op-code 0xffffffff that triggers
the user-defined error with a code 333 in the contract.