Mocking
Mocks are used in tests to exclude functionality that a test doesn't want to test. Mocks are used when it's difficult to test against an external component.
The Soroban Rust SDK makes it just as easy to test against a real contract as it does to test against a mock of a contract. In some ecosystems integration tests are avoided. Not in the Stellar ecosystem. See Integration Tests.
How to Write Tests with Mocksβ
The following is an example of a test that uses a mock, written to test the increment-with-pause contract. The contract has an increment
function that increases a counter value by one on every invocation. The contract depends on another contract that controls whether the increment functionality is paused.
The following tests set up the increment-with-pause
contract, as well as a mock pause contract, and invokes the increment contract's function several times under different conditions the pause contract is expected to be in.
The following test checks that when the pause contract is not paused, the increment contract functions.
#![cfg(test)]
use crate::{Error, IncrementContract, IncrementContractArgs, IncrementContractClient, Pause};
use soroban_sdk::{contract, contractimpl, Env};
mod notpaused {
use super::*;
#[contract]
pub struct Mock;
#[contractimpl]
impl Pause for Mock {
fn paused(_env: Env) -> bool {
false
}
}
}
#[test]
fn test_notpaused() {
let env = Env::default();
let pause_id = env.register(notpaused::Mock, ());
let contract_id = env.register(
IncrementContract,
IncrementContractArgs::__constructor(&pause_id),
);
let client = IncrementContractClient::new(&env, &contract_id);
assert_eq!(client.increment(), 1);
assert_eq!(client.increment(), 2);
assert_eq!(client.increment(), 3);
}
The following test checks that when the pause contract is paused, the increment contract function rejects attempts to increment.
#![cfg(test)]
use crate::{Error, IncrementContract, IncrementContractArgs, IncrementContractClient, Pause};
use soroban_sdk::{contract, contractimpl, Env};
mod paused {
use super::*;
#[contract]
pub struct Mock;
#[contractimpl]
impl Pause for Mock {
fn paused(_env: Env) -> bool {
true
}
}
}
#[test]
fn test_paused() {
let env = Env::default();
let pause_id = env.register(paused::Mock, ());
let contract_id = env.register(
IncrementContract,
IncrementContractArgs::__constructor(&pause_id),
);
let client = IncrementContractClient::new(&env, &contract_id);
assert_eq!(client.try_increment(), Err(Ok(Error::Paused)));
}
Most tests, whether you're writing unit, mocks, or integration tests, will look very similar to these tests. They'll do four things:
- Create an environment, the
Env
. - Register the contract(s) to be tested.
- Invoke functions using the generated client.
- Assert the outcome.
Mocking introduces assumptions about the behavior of another contract. Even if the contract publishes an interface that says it'll return a bool (true/false), contracts can return any type.
impl Pause {
fn paused(env: Env) -> ? { ? }
}
This is one reason why it's helpful to test and fuzz using real dependencies and to code defensively assuming any external contract call could cause your contract to fail.
See Integration Tests for how to test with real dependencies.
The Soroban Rust SDK handles contract calls defensively so that any unexpected error or unexpected type returned from the called contract will cause execution to stop. The SDK also provides methods to make these calls and to intercept error situations. See Making Cross-Contract Calls for more details.
Guides in this category:
ποΈ Unit Tests
Unit tests are small tests that test smart contracts.
ποΈ Mocking
Mocking dependency contracts in tests.
ποΈ Integration Tests
Integration testing uses dependency contracts instead of mocks.
ποΈ Fuzzing
Fuzzing and property testing to find unexpected behavior.
ποΈ Differential Tests
Differential testing detects unintended changes.
ποΈ Differential Tests with Test Snapshots
Differential testing using automatic test snapshots.
ποΈ Mutation Testing
Mutation testing finds code not tested.
ποΈ Test Authorization
Write tests that test contract authorization.
ποΈ Test Events
Write tests that test contract events.