Advanced Testing Techniques
This guide covers advanced techniques for testing smart contracts on OP_NET, including time progression simulation, error handling, and edge case testing.
Simulating Time and Block Progression
Testing time-dependent logic is critical for contracts with features like expirations or time locks.
- Progressing Blocks: Use
Blockchain.blockNumber
to simulate advancing the block number. - Adjusting Timestamps: Modify
Blockchain.medianTimestamp
to test time-sensitive contract logic.
import { Blockchain } from "@btc-vision/unit-test-framework";
Blockchain.blockNumber += 10n; // Simulate advancing 10 blocks
Blockchain.medianTimestamp += 600n; // Simulate 10 minutes forward (600 seconds)
Use Case
Testing time-based expirations:
await vm.it("should expire after 10 minutes", async () => {
Blockchain.medianTimestamp += 600n; // Simulate 10 minutes
const isExpired = await contract.checkExpiration(); // Example function to check expiration
Assert.expect(isExpired).toEqual(true);
});
Testing Error Handling and Reverts
Ensure that your contract behaves as expected when errors or invalid conditions occur.
Expecting Errors
Use Assert.expect().toThrow()
to test functions expected to throw an error or revert.
await vm.it("should not mint invalid amount", async () => {
const receiver = Blockchain.generateRandomAddress();
await Assert.expect(async () => {
await contract.mint(receiver, 0.01); // Invalid mint amount
}).toThrow();
});
Verifying Error Messages
Validate specific revert reasons or error messages:
await vm.it(
"should not transfer tokens if the sender has insufficient balance",
async () => {
const sender = Blockchain.generateRandomAddress();
const receiver = Blockchain.generateRandomAddress();
await Assert.expect(async () => {
await contract.transfer(sender, receiver, 100n);
}).toThrow("Insufficient balance");
}
);
Testing Edge Cases and Boundary Conditions
Stress your contract logic by testing with extreme values and edge cases.
Stress Testing with Extreme Values
Test the behavior of contracts under unusually high or low values.
await vm.it("should handle maximum token supply", async () => {
const receiver = Blockchain.generateRandomAddress();
const maxSupply = 1e8;
await Assert.expect(async () => {
await contract.mint(receiver, maxSupply);
}).toNotThrow();
});
Underflow and Overflow Conditions
Verify that the contract correctly handles arithmetic boundaries:
await vm.it("should prevent underflow", async () => {
await Assert.expect(async () => {
await contract.burn(receiver, 1); // Burn more than balance
}).toThrow("Insufficient balance");
});
await vm.it("should prevent overflow", async () => {
const maxSupply = 1e8;
await Assert.expect(async () => {
await contract.mint(receiver, maxSupply + 1); // Mint more than the max supply
}).toThrow("Max supply reached");
});
Best Practices
- Clearly describe the test scenario and expected outcomes.
- Cover all possible execution paths, including edge cases and error conditions.
- Use
Assert.expect().toThrow()
to verify error handling and reverts. - Simulate time progression and block advancement for time-sensitive logic.