Skip to content

Latest commit

 

History

History
118 lines (89 loc) · 3.46 KB

File metadata and controls

118 lines (89 loc) · 3.46 KB

End-to-end Testing for Stylus Contracts

This end-to-end testing crate provides affordances to test your contracts in a blockchain environment.

This crate is currently coupled to nitro-testnode.

Usage

Refer to our end-to-end tests GitHub workflow for a working example of a full tests suite using the e2e crate.

Accounts

Decorate your tests with the test procedural macro: a thin wrapper over tokio::test that sets up Accounts for your test.

#[e2e::test]
async fn accounts_are_funded(alice: Account) -> eyre::Result<()> {
    let balance = alice.wallet.get_balance(alice.address()).await?;
    let expected = parse_ether("10")?;
    assert_eq!(expected, balance);
    Ok(())
}

An Account is a thin wrapper over a [PrivateKeySigner] and an alloy provider with a WalletFiller. Both of them are connected to the RPC endpoint defined by the RPC_URL environment variable. This means that a Account is the main proxy between the RPC and the test code.

All accounts start with 10 ETH as balance. You can have multiple accounts as parameters of your test function, or you can create new accounts separately:

#[e2e::test]
async fn foo(alice: Account, bob: Account) -> eyre::Result<()> {
    let charlie = Account::new().await?;
    // ...
}

Contracts

We use [cargo-stylus] to deploy contracts to the blockchain. Deployer type exposes Deployer::deploy method that abstracts away the mechanism used in our workflow.

Account type exposes Account::as_deployer method that returns Deployer type. It will facilitate deployment of the contract marked with the #[entrypoint] macro. Then you can configure deployment with default constructor:

let contract_addr = alice.as_deployer().deploy().await?.contract_address;

Or with a custom constructor. Note that you can use the e2e::constructor! macro to instantiate a valid constructor struct.

let ctr = e2e::constructor!(
    "Token".to_owned(),
    "TKN".to_owned()
);
let receipt = alice
    .as_deployer()
    .with_constructor(ctr)
    .deploy()
    .await?;

Then altogether, your first test case can look like this:

#[e2e::test]
async fn constructs(alice: Account) -> eyre::Result<()> {
    let ctr = e2e::constructor!(
        "Token".to_owned(),
        "TKN".to_owned()
    );
    let contract_addr = alice
        .as_deployer()
        .with_constructor(ctr)
        .deploy()
        .await?
        .contract_address;
    let contract = Erc20::new(contract_addr, &alice.wallet);

    let name = contract.name().call().await?.name;
    let symbol = contract.symbol().call().await?.symbol;

    assert_eq!(name, TOKEN_NAME.to_owned());
    assert_eq!(symbol, TOKEN_SYMBOL.to_owned());
    Ok(())
}

Notice

We maintain this crate on a best-effort basis. We use it extensively on our own tests, so we will continue to add more affordances as we need them.

That being said, please do open an issue to start a discussion, keeping in mind our code of conduct and contribution guidelines.

Security

Refer to our Security Policy for more details.