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.