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 \
--func contracts/contract.func \
--func-std path/to/project/contracts/stdlib.func \
--fift-std path/to/fiftstdlib
Output
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.fc
npm test tests/TestFile.spec.fc
Example
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.