Skip to content

Commit 006373e

Browse files
committed
Add documentation on how to do unit tests with web3.py
1 parent f4a7d8c commit 006373e

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

docs/examples.rst

+22
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,25 @@ When someone has an allowance they can transfer those tokens using the
449449

450450

451451
.. _ERC20: https://github.com/ethereum/EIPs/blob/7f4f0377730f5fc266824084188cc17cf246932e/EIPS/eip-20.md
452+
453+
454+
Contract Unit Tests in Python
455+
-----------------------------
456+
457+
Here is an example of how one can use the pytest platform in python,web3.py,
458+
eth-tester, and pyevm to perform unit tests entirely in python without any
459+
additional need for a full featured ethereum node/client. To install needed
460+
dependencies you can use the pinned extra for eth_tester in web3 and pytest:
461+
462+
.. code-block:: bash
463+
pip install web3[tester] pytest
464+
465+
Once you have an environment set up for testing, you can then write your tests
466+
like so:
467+
468+
.. include:: ../tests/core/contracts/test_contract_example.py
469+
:code: python
470+
:start_line: 1
471+
472+
473+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# This file is used by the documentation as an example of how to write unit tests with web3.py
2+
import logging
3+
import pytest
4+
5+
from web3 import (
6+
EthereumTesterProvider,
7+
Web3,
8+
)
9+
10+
11+
@pytest.fixture
12+
def tester_provider():
13+
return EthereumTesterProvider()
14+
15+
16+
@pytest.fixture
17+
def eth_tester(tester_provider):
18+
return tester_provider.ethereum_tester
19+
20+
21+
@pytest.fixture
22+
def w3(tester_provider):
23+
return Web3(tester_provider)
24+
25+
26+
@pytest.fixture
27+
def deploy_contract(eth_tester, w3):
28+
deploy_address = str(eth_tester.get_accounts()[0])
29+
w3.personal.unlockAccount(deploy_address, "")
30+
31+
# For simplicity of this example we statically define the
32+
# contract code here. You might read your contracts from a
33+
# file, or something else to test with in your own code
34+
#
35+
# pragma solidity^0.5.3;
36+
#
37+
# contract Foo {
38+
#
39+
# string public bar;
40+
# event barred(string _bar);
41+
#
42+
# constructor() public {
43+
# bar = "hello world";
44+
# }
45+
#
46+
# function setBar(string memory _bar) public {
47+
# bar = _bar;
48+
# emit barred(_bar);
49+
# }
50+
#
51+
# }
52+
53+
abi = """[{"anonymous":false,"inputs":[{"indexed":false,"name":"_bar","type":"string"}],"name":"barred","type":"event"},{"constant":false,"inputs":[{"name":"_bar","type":"string"}],"name":"setBar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"bar","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]""" # noqa: E501
54+
# This bytecode is the output of compiling with
55+
# solc version:0.5.3+commit.10d17f24.Emscripten.clang
56+
bytecode = """608060405234801561001057600080fd5b506040805190810160405280600b81526020017f68656c6c6f20776f726c640000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6103bb806101166000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c01000000000000000000000000000000000000000000000000000000009004806397bc14aa14610058578063febb0f7e14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b61024c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac9291906102ea565b507f5f71ad82e16f082de5ff496b140e2fbc8621eeb37b36d59b185c3f1364bbd529816040518080602001828103825283818151815260200191508051906020019080838360005b8381101561020f5780820151818401526020810190506101f4565b50505050905090810190601f16801561023c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a150565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102e25780601f106102b7576101008083540402835291602001916102e2565b820191906000526020600020905b8154815290600101906020018083116102c557829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032b57805160ff1916838001178555610359565b82800160010185558215610359579182015b8281111561035857825182559160200191906001019061033d565b5b509050610366919061036a565b5090565b61038c91905b80821115610388576000816000905550600101610370565b5090565b9056fea165627a7a72305820ae6ca683d45ee8a71bba45caee29e4815147cd308f772c853a20dfe08214dbb50029""" # noqa: E501
57+
contract = w3.eth.contract(abi=abi, bytecode=bytecode)
58+
tx_hash = contract.constructor().transact({
59+
'from': deploy_address,
60+
'gas': 2000000
61+
})
62+
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash, 180)
63+
return w3.eth.contract(abi=abi, address=tx_receipt.contractAddress)
64+
65+
66+
def test_hello_world(deploy_contract):
67+
contract_object = deploy_contract
68+
hw = contract_object.functions.bar().call()
69+
logging.info(hw)
70+
assert hw == "hello world"
71+
72+
73+
def test_set_bar(eth_tester, w3, deploy_contract):
74+
contract_object = deploy_contract
75+
account1 = str(eth_tester.get_accounts()[1])
76+
event_filter = contract_object.events.barred().createFilter(
77+
fromBlock='latest') # argument_filters={'args': '_bar'})
78+
79+
assert eth_tester.get_balance(account1) == 1000000000000000000000000
80+
81+
tx_hash = contract_object.functions.setBar(
82+
"ethereum is the best").transact({
83+
'from': account1,
84+
'gas': 2000000
85+
})
86+
w3.eth.waitForTransactionReceipt(tx_hash, 180)
87+
88+
hw = contract_object.functions.bar().call()
89+
assert hw == "ethereum is the best"
90+
assert eth_tester.get_balance(
91+
account1
92+
) != 1000000000000000000000000 # gotta spend money on tx fees to call setBar
93+
94+
event_list = event_filter.get_new_entries()
95+
assert len(event_list) == 1
96+
event = event_list[0]
97+
print(event)
98+
assert event['args']['_bar'] == "ethereum is the best"

0 commit comments

Comments
 (0)