Skip to main content

Used to auto-generate unit tests for smart contracts using the Forge framework.

Project description

Olympix Test Generator

Olympix Test Generator is a Python-based utility tool to streamline the process of generating unit tests for Solidity smart contracts. These unit tests are tailored to be compatible with Foundry, a specialized tool for writing unit tests in Solidity.

Prerequisites

  • Python 3.7 or later
  • Solidity 0.8.0 or later

Installation

Install the Olympix Test Generator through pip:

pip install olympix-test-generator

Configuration

Prior to running the script, you'll need to set the OLYMPIX_API_KEY environment variable in your system.

For Unix or Linux:

export OLYMPIX_API_KEY=your-api-key

For Windows:

setx OLYMPIX_API_KEY "your-api-key"

Usage

Olympix Test Generator offers a command-line interface for creating tests.

Initiate the test generation process with:

olympix generate

Upon executing the command, the tool will guide you through the steps required to generate the test.

A test contract will be created either in the same directory as the chosen smart contract, or in an adjacent test folder, should one exist. Existing files will remain unaffected. The generated tests are also displayed on the console.

For optimal results, generate tests for one function at a time.

Sure, here's an improved version of the section:

Example - Contract and Generated Tests

In this section, we provide two examples of how the Olympix Test Generator works with different contracts: a smaller contract, Counter.sol, and a larger contract, MyToken.sol.

Example 1: Counter.sol

The Counter.sol contract is a simple contract that counts a number. The associated Counter.t.sol test contract is generated using our tool. All tests pass with 100% code coverage.

Counter.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}

Generated Test: Counter.t.sol

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Counter} from "../src/Counter.sol";
import "forge-std/Test.sol";

contract CounterTest is Test {
    Counter counter;

    function setUp() public {
        counter = new Counter();
    }

    function test_initialNumber() public {
        assertEq(counter.number(), 0);
    }

    function test_setNumber() public {
        counter.setNumber(5);
        assertEq(counter.number(), 5);
    }

    function test_increment() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }
}

Example 2: MyToken.sol

MyToken.sol is a more complex contract that implements a basic ERC20 token. Due to its complexity, we generate tests specifically for the approve function, resulting in the MyTokenApprove.t.sol test contract. We recommend this approach—generating tests for individual functions—for larger, more complex contracts.

MyToken.sol

// SPDX-License-Identifier: MIT
//pragma solidity >=0.4.22 <0.9.0;
pragma solidity 0.8.2;

contract MyToken {
    // Mapping from account addresses to current balance.
    mapping(address => uint256) private _balances;

    // Mapping from account addresses to a mapping of spender addresses to an amount of allowance.
    mapping(address => mapping(address => uint256)) private _allowances;

    // Name of the token
    string private _name;

    // Symbol of the token
    string private _symbol;

    // Total of the token supply
    uint256 private _totalSupply;

    constructor(string memory name_, string memory symbol_, uint256 totalSupply_) {
        _name = name_;
        _symbol = symbol_;
        _totalSupply = totalSupply_;
        _balances[msg.sender] = totalSupply_;
    }

    function name() public view returns(string memory) {
        return _name;
    }

    function symbol() public view returns(string memory) {
        return _symbol;
    }

    function totalSupply() public view returns(uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns(uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public returns(bool) {
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[msg.sender] >= amount, "ERC20: transfer amount exceeds balance");

        _balances[msg.sender] -= amount;
        _balances[recipient] += amount;

        emit Transfer(msg.sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public returns(bool) {
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[msg.sender][spender] = amount;
        
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns(uint256) {
        return _allowances[owner][spender];
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns(bool) {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance");
        require(_allowances[sender][msg.sender] >= amount, "ERC20: transfer amount exceeds allowance");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        _allowances[sender][msg.sender] -= amount;

        emit Transfer(sender, recipient, amount);
        return true;
    }

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

Generated Test: MyTokenApprove.t.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {MyToken} from "../src/MyToken.sol";
import "forge-std/Test.sol";

contract MyTokenTest is Test {
    MyToken myToken;
    address spender;

    function setUp() public {
        myToken = new MyToken("My Token", "MTK", 1000000);
        spender = address(0x123);
    }

    function test_approve() public {
        uint256 amount = 500;
        bool success = myToken.approve(spender, amount);
        assert(success);

        uint256 allowance = myToken.allowance(address(this), spender);
        assertEq(allowance, amount, "Allowance does not match approved amount");
    }
}

In both cases, the Olympix Test Generator produces compact and efficient test contracts, which can help you ensure the reliability and security of your Solidity contracts.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

olympix-test-generator-1.2.2.tar.gz (6.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

olympix_test_generator-1.2.2-py3-none-any.whl (6.0 kB view details)

Uploaded Python 3

File details

Details for the file olympix-test-generator-1.2.2.tar.gz.

File metadata

  • Download URL: olympix-test-generator-1.2.2.tar.gz
  • Upload date:
  • Size: 6.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.8.10

File hashes

Hashes for olympix-test-generator-1.2.2.tar.gz
Algorithm Hash digest
SHA256 f1d8a09886b3dff730c8e8d605881ab2b9a2226e8393f5536a4cf34963c667a5
MD5 facb3deba84a6a025492ab3e747d52f9
BLAKE2b-256 620d8667b21b874522052f10ad3a7ea85706a21144c4803129aaf129bd81b53d

See more details on using hashes here.

File details

Details for the file olympix_test_generator-1.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for olympix_test_generator-1.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 10b0ffc13be96ae4888349f9ae60eac7af0d1c3f13a11f9ff886b755e77ff8de
MD5 6303d7732514cfe210de184ab593c88c
BLAKE2b-256 193e606125b715a5c22b5137b35b2ca9cf86ade67cbbbda01bbb5668f38f0642

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page