Local Testing
Learn how to locally test Zephyr programs.
Mercury's Zephyr comes equipped with all the tooling you need to test and debug your programs locally. This includes also being able to craft manually custom situations and edge cases asserting the correct funcitoning of all of the code blocks in your program.
In this section of the documentation you'll learn everything you need to make sure your program as a whole works correctly.
Understanding Zephyr Tests
Due to the way Zephyr works and it's target utility, coming up with a great testing experience has not been an easy task. We believe that the current state (which will keep improving) is already quite a great developer experience, that said there are a few concepts that need to be comprehended before starting to test your programs:
Database connector. The testutils provide a simple, non production-ready zephyr postgres database connector. This means that in order to run tests that write/read from tables you'll need a working postgresql setup on your machine. In fact, during the test setup you'll have to provide a valid postgres connection string. If you're testing on a non user-facing machine such as your personal computer it can be a common practice to connect to a default postgres string such as
postgres://postgres:postgres@localhost:5432
assuming that the user exists with such password.[Currently] No ledger read functionality. Since the purpose of local testing is to test without needing to setup services like stellar-core, ability to read from the ledger is not currently supported in tests. However, we are working on bringing a ledger adapter on tests too where the developer can set the ledger entries they'll access into the testing environment.
Building ledger transitions. As one could imagine, the hardest part about providing a consistent (assuming a correctly setup database) testing environment in Zephyr is the data the VM is fed. In Mercury servers, stellar core communicates directly with the ZephyrVMs spawned at each ledegr close providing them with the metadata pertinent to the ledger close (soroban events, transactions, changed entries, etc). In a local testing environment this is not possible, and even if you manually connected a local stellar core instance to Zephyr you'd have non-consistent testing, and difficulty in feeding the VM with the data you want to test (you'd have to perform on chain actions such as contract calls), and testing edge cases is also complex. To counter this and offer a smooth testing experience, we've coupled Zephyr's testutils with a new crate we've just created and are going to further improve which allows to craft custom ledger transition/meta objects.
Cargo.toml
Before starting, make sure that your Cargo.toml is correctly configured to add the testutils feature to the zephyr-sdk in the dev depencencies, plus add the ledger-meta-factory, tokyo and xdr libraries which will be additional crates needed for testing:
Note that if you add the testutils flag on the dependencies instead of dev dependencies your program will not compile.
The TestHost Object
The TestHost
object exported by the zephyr-sdk
's testutils will be the entry point for your tests management. It will provide you handles to:
set up your database and load the ephemeral tables that your program will use.
manage and invoke Zephyr programs enabling also to set the custom transitions if needed.
Below is how a typical Zephyr test function would look like:
We create an util function to create a transition that contains certain soroban events which our program relies on.
Setup the database and load the program from the built binary.
Load the transition from point 1 into the program.
Create all the required tables with the respective columns.
Assert that the table is empty.
Invoke the program and assert that both on the host (outer result) and on the guest (inner result) everything ran as expected.
Check again the number of rows in the table and assert that our entry was correctly added.
Close the connection to the database and drop all the ephemeral tables.
If your test panics before db.close().await
then the test won't have cleared and dropped your tables. You'll need to manually connect to the database (psql $CONNECTION_STRING
and drop the tables DROP TABLE zephyr_$TABLE_NAME
).
If you're running multiple tests in the same execution, make sure to run them single-threadedly since the execution of one test could impact the execution of another test if they're connected to the same database: cargo test -- --exact --nocapture --test-threads 1
Resources
Last updated