With Woke 2.0, we introduced an updated version of our Python-based development and testing framework. 

Let’s look closely and find out how it differs from other Python-based testing frameworks and what are Woke’s advantages. 

Pytypes

Woke testing framework builds on the so-called pytypes. These are automatically generated Python files representing Solidity contracts, structs, enums, etc. in the Python world. Files in the pytypes directory are organized in such a way so that it reflected the directory structure of the original Solidity sources. pytypes not only provide auto-completion and type hints support but also work as standalone objects that do not need any external files (Solidity source files, for example). All necessary information is included in pytypes.

To generate pytypes for a project in a current working directory, one can you the woke init pytypes command. An optional -w or --watch option launches the pytypes generator in watch mode. Solidity source files are watched for changes and pytypes are automatically re-generated.

Woke testing framework was inspired by Brownie, thus the way it interacts with the contracts may be familiar to Brownie users. However, as Woke learned from Brownie’s mistakes, there are some crucial differences and improvements.

Getting started with Woke 

First, a contract must be imported from pytypes. Importing contracts from different namespaces (modules) is essential as it helps to avoid name clashes.

Like Brownie, Woke testing framework uses pytest to collect tests and offer various features like fixtures. From the very beginning, Woke testing framework was designed to support multichain (cross-chain) testing. Still, it is expected that most of the tests will perform single-chain testing. There is a default_chain global object that should be used for single-chain testing. It can be imported from the woke.testing module along with all other objects one may need.

default_chain can be either connected to an already running dev chain or launch a new dev chain.

With the default configuration, Woke uses Anvil as the development chain. Anvil is a part of the Foundry project. It is highly recommended to keep Anvil up-to-date: Woke development team co-operates with Anvil developers to ensure a seamless experience when writing tests.

With default_chain connected to a dev chain, it is easy to deploy the first contract using Woke testing framework.

Main differences from Brownie

The previous example shows another difference from Brownie. Any additional data that modify a transaction or call are passed using keyword arguments instead of a dictionary. By default, pure and view Solidity functions (including public state variable getters) are called using eth_call, i.e. a transaction is not sent. The request_type keyword can override this. 

Another significant difference from Brownie is that pytypes function calls that send a transaction return the function return value instead of a transaction object. Setting return_tx=True in a function call or even generating pytypes with return_tx implicitly set: woke init pytypes --return-tx can override this. 

This example may show yet another advantage of Woke testing framework: it was designed to be very fast. There are a few things to keep in mind in order to achieve maximum performance:

  • Anvil should be used whenever possible
  • Transaction events should be accessed only when necessary; working with tx.events may be slow in some cases
  • Transactions should be mainly sent from pre-generated accounts (default_chain.accounts); sending a transaction from any other account is possible but at the expense of poorer performance.

Thanks to pytypes, transaction events can be worked with using native Python objects, dataclasses. events is the property of a transaction object received either by setting return_tx=True or by defining tx_callback.

tx_callback is automatically called for transactions that do not have return_tx=True set. User-defined errors are also generated in the form of dataclasses. Additionally, there are two internal types of errors: Error(str) and Panic(int). Errors are automatically raised as exceptions with return_tx set to False. There are helper context managers, must_revert and may_revert, to make handling exceptions easier.

The mentioned above covers the key differences between Woke and Brownie. Be sure to check out our documentation and follow our Twitter to stay updated.