To use TestTools you have to install them and specify location of hive executables. Whole process is described below.
Full installation (recommended for testers and developers)
-
Install in virtual environment
cd ~/hive/tests/ # Go to tests/ directory of hive repository python3.8 -m venv venv/ # Create virtual environment in venv/ directory source venv/bin/activate # Activate it pip install poetry # Install poetry cd test_tools/ # Go to TestTools directory poetry install # Install TestTools with dev-dependencies
To deactivate virtual environment run:
deactivate
ℹ Hint: If you want to use PyCharm, you may be interested in the integration instructions below.
Add new, local interpreter:
Provide the path to python in the previously created virtual environment:
-
Install in your operating system scope (not recommended)
Enter following command in terminal:
python3.8 -m pip install poetry # Install poetry cd ~/hive/tests/test_tools/ # Go to TestTools directory python3.8 -m poetry install # Install TestTools with dev-dependencies
Installation will include additional tools for code quality checking and pre-commit
framework for git hooks managing.
You can initialize it with the following line:
source ~/hive/tests/venv/bin/activate # Active the previously created virtual environment
cd ~/hive/tests/test_tools/ # Go to TestTools directory
pre-commit install # Install the pre-commit script
ℹ️ Hint: If for some reason you want to bypass the pre-commit hooks, use the --no-verify
flag.
This will skip all analysis, so you will be able to trigger CI/CD pipeline (e.g. to run tests on WIP code) without the
need to ensure the production quality of the code.
git commit --no-verify
ℹ️ Hint: When using an IDE-integrated terminal, you shouldn't have the problems described in this section, as most IDEs support automatic activation of venv.
This tool can be useful if you have encountered one of the following errors:
Click to expand error messages
`pre-commit` not found. Did you forget to activate your virtualenv?
$ git commit
check for added large files..............................................Passed
check for merge conflicts................................................Passed
check yaml...........................................(no files to check)Skipped
check json...........................................(no files to check)Skipped
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
fix double quoted strings............................(no files to check)Skipped
pretty format json...................................(no files to check)Skipped
lint all sources with pylint.............................................Failed
- hook id: pylint-sources
- exit code: 1
Traceback (most recent call last):
File "/home/dev/.local/bin/pylint", line 5, in <module>
from pylint import run_pylint
ModuleNotFoundError: No module named 'pylint'
lint user handles documentation with pylint..............................Failed
- hook id: pylint-handles
- exit code: 1
Traceback (most recent call last):
File "/home/dev/.local/bin/pylint", line 5, in <module>
from pylint import run_pylint
ModuleNotFoundError: No module named 'pylint'
The errors above were caused by omitted activation of venv. The pre-commit
hooks for their checks require additional
dependencies (e.g. pylint
) installed in a virtual
environment. It is easy to forget to activate it manually every time. This script activates the virtual environment
automatically every time you enter the directory containing it.
Follow the instructions below:
-
Get the script:
curl -s -o ~/.virtualenv-autodetect.sh https://raw.githubusercontent.com/egilewski/virtualenv-autodetect/29c814f4e5b6f32a7b1952727cf112a13f34327d/virtualenv-autodetect.sh
-
Add the following line to the end of your
.bashrc
,.bash-profile
or.zshenv
file:source ~/.virtualenv-autodetect.sh
-
Restart your terminal.
Lightweight installation (if you want only to run tests, without contributing)
-
Install in virtual environment
cd ~/hive/tests/test_tools/ # Go to repository root directory python3.8 -m venv venv/ # Create virtual environment in venv/ directory source venv/bin/activate # Activate it pip install -e ~/hive/tests/test_tools # Install TestTools
To deactivate virtual environment run:
deactivate
ℹ Hint: If you want to use PyCharm, you may be interested in the integration instructions below.
Add new, local interpreter:
Provide the path to python in the previously created virtual environment:
-
Install in your operating system scope (not recommended)
Enter following command in terminal:
python3.8 -m pip install -e ~/hive/tests/test_tools/ # Install TestTools
Define environment variable HIVE_BUILD_ROOT_PATH
with path to hive build directory (containing hived, cli_wallet
and other executables). Add entry HIVE_BUILD_ROOT_PATH="/home/dev/hive/build"
to /etc/environment
and restart
computer.
If you don't want to modify content of your `/etc/environment` file...
...then you have to set this variable locally before every run of script, which uses TestTools. It can be done as in examples below:
HIVE_BUILD_ROOT_PATH="/home/dev/hive/build" pytest
HIVE_BUILD_ROOT_PATH="/home/dev/hive/build" python3 your_script.py
You can run testnet with node configured for blocks production and attached wallet with such simple script:
import test_tools as tt
if __name__ == '__main__':
node = tt.InitNode()
node.run()
wallet = tt.Wallet(attach_to=node)
Node and wallet has api
member which allows for communication with them. You can communicate with node and wallet with
following syntax:
# Example node calls
node.api.condenser.get_key_references(['TST5P8syqoj7itoDjbtDvCMCb5W3BNJtUjws9v7TDNZKqBLmp3pQW'])
node.api.database.list_witnesses(start=None, limit=100, order='by_name')
# Example wallet calls
wallet.api.create_account('initminer', 'john', '{}')
wallet.api.get_account('john')
Use your IDE's code completion to get hints for wallet api calls like below. IDE should help you to write method names, but also parameters.
Creation and initialization of nodes are simplified by predefined node types. Node types hide a lot of configuration stage and make tests more readable, because node purpose is directly expressed in code (read more). Following node types are supported:
- init node,
- witness node,
- api node,
- full api node,
- raw node.
This is how network consisting of init, witness and api node can be defined:
network = tt.Network()
init_node = tt.InitNode(network=network)
witness_node = tt.WitnessNode(witnesses=[f'w{i}' for i in range(10)], network=network)
api_node = tt.ApiNode(network=network)
Node has config
member which allow for editing hived config.ini file. You can configure node in following way:
node.config.enable_stale_production = True
node.config.required_participation = 0
node.config.plugin.extend(['p2p', 'witness'])
⚠️ Type support is not completed yet. Not all config entries types are set correctly. At the moment most of them are treated as strings. So you have to write like:# Note that all are strings node.config.market_history_bucket_size = '[15,60,300,3600,86400]' node.config.webserver_thread_pool_size = '32'
Provides support for Python types. You can write:
if node.config.enable_stale_production and node.config.required_participation < 20:
...
because type of node.config.enable_stale_production
is bool
and type of node.config.required_participation
is int
.
You can select them in python script, via command line arguments, environment variables or by executables installation (read more).
During tests nodes generates a lot of files. In most cases some of these files are unneeded and are automatically removed by library. You can configure this behavior for your needs (read more).
Some tests requires multiple accounts creation. To perform operations on them keys are required. TestTools provides
support for key generation with Account
class. You only need to provide account name. Generated account contains
member variables private_key
and public_key
, which can be used in tests. Optionally you can specify secret
parameter, which affects generated keys.
# Simple account creation example
account = tt.Account('John')
print(account.private_key) # Prints: 5KSJQHSBh4vxZVaY2fi3vbhDbkkg7C74pE4S3bigEQyct2RqMDf
print(account.public_key) # Prints: TST8FukVPod6riKr2mg94hhDanCzCYvivJtPdpcUVnEChaJ5N9QbC
# Inline usage example
print(tt.Account('initminer').private_key) # Prints: 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n
If you need to create many accounts, (e.g. more than 10 000), it might be slow using method described above. For multiple accounts creation use method showed below:
tt.Account.create_multiple(100_000, 'example') # Optimized version of: [Account('example-{i}') for i in range(100_000)]
TestTools provides support for sending single transaction containing multiple operations. It is helpful for optimizing scripts which send multiple transactions and wait 3 seconds for every transaction confirmation. When sending these operations in one transaction, you wait for only one confirmation, so your script executes much faster. You can do it with following syntax:
with wallet.in_single_transaction():
wallet.api.create_account('initminer', account, '{}')
wallet.api.transfer('initminer', account, amount, 'memo')
In above example operations create_account
and transfer
are sent in single transaction during exiting from "with"
statement.
Implementation is very flexible and allows for using python control statements (ifs, loops), functions which sends wallet api calls and so on. See example below showing for-loop use case during transaction preparation.
accounts_and_balances = {
'first': tt.Asset.test(100),
'second': tt.Asset.test(200),
'third': tt.Asset.test(300),
}
with wallet.in_single_transaction():
for account, amount in accounts_and_balances.items():
wallet.api.create_account('initminer', account, '{}')
wallet.api.transfer('initminer', account, amount, 'memo')
If you want to read response, you can name a returned context manager and call its get_response
method after
exiting with
context, like this:
with wallet.in_single_transaction() as transaction:
...
response = transaction.get_response()