Skip to content

Commit 9abb135

Browse files
authored
Addition of new testing content to the smart contracts section along with the addition of an overview of the Litmus tool (#540)
1 parent 6be364e commit 9abb135

File tree

7 files changed

+372
-0
lines changed

7 files changed

+372
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_testing
3+
title: Testing
4+
description: Provides an overview of the testing tools and techniques available to developers
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/introduction
7+
---
8+
9+
# Testing
10+
11+
Testing is a critical component of smart contract development. Proper testing ensures that changes to the contract codebase can be integrated smoothly without introducing bugs or disrupting existing functionality. In CosmWasm, a well-designed contract should have a comprehensive set of tests, divided into two primary categories: **Unit Testing** and **Integration Testing**.
12+
13+
## Unit testing
14+
15+
Unit testing is essential for verifying the correctness of individual components of your smart contract. It allows you to catch bugs and issues early in the development process, ensuring that each part of your contract functions as expected.
16+
17+
### Writing unit tests
18+
19+
To write unit tests in Rust for CosmWasm smart contracts, use the `#[test]` attribute to mark test functions. These functions should be placed in a `tests` module within your smart contract's source code, typically using a `#[cfg(test)]` attribute to compile them only during testing. Rust provides macros like `assert!`, `assert_eq!`, and `assert_ne!` to validate your code's behavior against expected outcomes.
20+
21+
#### Example
22+
23+
```rust
24+
#[cfg(test)]
25+
mod tests {
26+
use super::*;
27+
use cosmwasm_std::testing::{mock_env, mock_info};
28+
use cosmwasm_std::{from_binary, Uint128};
29+
30+
#[test]
31+
fn test_transfer_funds_success() {
32+
let mut deps = mock_dependencies();
33+
let env = mock_env();
34+
let info = mock_info("sender", &[]);
35+
36+
let msg = ExecuteMsg::Transfer {
37+
recipient: "recipient".to_string(),
38+
amount: Uint128::new(100),
39+
};
40+
let res = execute(deps.as_mut(), env, info, msg).unwrap();
41+
assert_eq!(res.messages.len(), 1);
42+
}
43+
}
44+
```
45+
46+
### Running unit tests
47+
48+
To run your unit tests, execute the following command in your terminal:
49+
50+
```bash
51+
RUST_BACKTRACE=1 cargo test
52+
```
53+
54+
After compilation, you should see output similar to:
55+
56+
```text
57+
running 15 tests
58+
test coin_helpers::test::assert_sent_sufficient_coin_works ... ok
59+
test tests::test_module::proper_init_no_fees ... ok
60+
...
61+
test result: ok. 15 passed; 0 failed; 0 ignored; finished in 0.00s
62+
```
63+
64+
Setting `RUST_BACKTRACE=1` provides full stack traces for any errors encountered during testing, which is particularly useful for debugging. This option only works for unit tests, which test native Rust code rather than the compiled WebAssembly (Wasm).
65+
66+
### Mocking
67+
68+
Mocking and dependency injection are techniques used to isolate specific parts of your smart contract during testing. By creating mock objects or functions to replace the actual dependencies of your contract, you can control their behavior during tests. This approach allows you to simulate various conditions and scenarios without affecting the actual contract state or dependencies.
69+
70+
#### Example
71+
72+
```rust
73+
fn mock_dependencies() -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
74+
let custom_querier = MockQuerier::new(&[]);
75+
OwnedDeps {
76+
storage: MockStorage::new(),
77+
api: MockApi::default(),
78+
querier: custom_querier,
79+
}
80+
}
81+
```
82+
83+
### Best practices for unit testing
84+
85+
- **Organize Tests**: Group tests by functionality and place them in separate modules or files.
86+
- **Descriptive Naming**: Use descriptive names for tests, such as `test_transfer_funds_success` or `test_invalid_name_rejection`.
87+
- **Focus on Simplicity**: Write clear, concise tests that focus on individual components of the smart contract.
88+
- **Test Edge Cases**: Ensure your smart contract can handle various scenarios by testing edge cases and failure conditions.
89+
- **Independence**: Keep tests independent so that the failure of one test does not impact others.
90+
91+
## Integration testing with cw-multi-test
92+
93+
The [cw-multi-test](https://crates.io/crates/cw-multi-test) package provides a powerful way to test your smart contracts in a simulated blockchain environment without fully deploying them onto a testnet. This package allows you to perform contract-to-contract and contract-to-bank interactions within a controlled test environment.
94+
95+
We've created an entire section covering cw-multi-test that can be viewed [here](/developers/smart-contracts/testing/cw-multi-test/introduction).
96+
97+
## Best practices for integration testing
98+
99+
- **Mock Everything**: Ensure that all contracts and services your contract interacts with are mocked out.
100+
- **Reuse Mocks**: Define reusable functions for wrapping and mocking contracts to simplify your test code.
101+
- **Test Scenarios Thoroughly**: Test a variety of scenarios, including edge cases, to ensure your contracts behave correctly in all situations.
102+
103+
## Conclusion
104+
105+
By leveraging unit tests and integration tests with **cw-multi-test**, you can ensure that your CosmWasm smart contracts are reliable, secure, and ready for deployment. Use the best practices outlined here to create a robust testing suite that covers all aspects of your contract's functionality.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-introduction
3+
title: Introduction
4+
description: An introduction to CW-Multi-Test
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/testing
7+
---
8+
9+
# MultiTest
10+
11+
**`MultiTest`** is a testing tool designed to facilitate multi-contract interactions within the
12+
CosmWasm ecosystem. Its primary focus is on providing developers with a robust framework for
13+
off-chain testing of complex smart contract interactions and operations involving various Cosmos
14+
modules.
15+
16+
::alert{variant="info"}
17+
**`MultiTest`** is a blockchain **SIMULATOR**, allowing tested smart contracts to interact as if
18+
they were operating on a real blockchain.
19+
::
20+
21+
The most valuable advantages of using **`MultiTest`** is that it allows for testing and debugging
22+
smart contracts with access to the Rust source code and eliminates the need to run a complete
23+
blockchain node to begin designing the functionality of the contract. Additionally, **`MultiTest`**
24+
enables the execution of tests significantly faster than on a real blockchain, as it bypasses the
25+
overhead associated with network consensus and block production. This results in a more efficient
26+
development cycle, allowing for quicker iterations and faster identification of issues, even before
27+
the smart contract is deployed on the blockchain.
28+
29+
While **`MultiTest`** is a blockchain **SIMULATOR**, it may happen, that the behavior of the real
30+
blockchain might slightly differ in some edge cases.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-installation
3+
title: Installation
4+
description: How to install CW-Multi-Test
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/testing
7+
---
8+
9+
# Installation
10+
11+
**`MultiTest`** is as a [Rust](https://www.rust-lang.org) library named [cw-multi-test](https://crates.io/crates/cw-multi-test), and is hosted on [crates.io](https://crates.io).
12+
13+
## Usage
14+
15+
To use **`MultiTest`** in your project, simply add it as a **development dependency** to
16+
**Cargo.toml** file:
17+
18+
```toml filename="Cargo.toml" copy
19+
[dev-dependencies]
20+
cw-multi-test = "2"
21+
```
22+
23+
<br />
24+
25+
::alert{variant="info"}
26+
**`MultiTest`** is a **TESTING** library and should **ALWAYS** be added to your project as a
27+
**DEVELOPMENT DEPENDENCY** in section **`[dev-dependencies]`** of the **Cargo.toml** file.
28+
::
29+
30+
<br />
31+
32+
::alert{variant="info"}
33+
**`MultiTest`** **IS NOT** designed to be used in production code on a real-life blockchain.
34+
::
35+
36+
## Prerequisities
37+
38+
### Rust and Cargo
39+
40+
The only prerequisite to test smart contracts using **`MultiTest`** is having [Rust and Cargo](https://www.rust-lang.org/tools/install) installed.
41+
42+
::alert{variant="info"}
43+
We recommend installing Rust using the official [rustup installer](https://rustup.rs). This makes it easy to stay on
44+
the most recent version of Rust and Cargo.
45+
::
46+
47+
### Tarpaulin and cargo-nextest
48+
49+
Optionally, you may want to install [Tarpaulin](https://github.com/xd009642/tarpaulin) for measuring code coverage, and [cargo-nextest](https://nexte.st) for running tests faster with a clean and beautiful user interface.
50+
51+
Installing **Tarpaulin**:
52+
53+
```shell copy filename="TERMINAL"
54+
cargo install cargo-tarpaulin
55+
```
56+
57+
Installing **cargo-nextest**:
58+
59+
```shell copy filename="TERMINAL"
60+
cargo install cargo-nextest
61+
```
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-features
3+
title: Features
4+
description: Features of CW-Multi-Test
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/testing
7+
---
8+
9+
# Features
10+
11+
All **`MultiTest`** features are listed in the table below.
12+
13+
The **Default&nbsp;implementation** column indicates whether the feature has a default
14+
implementation in **`MultiTest`** that simulates the functionality of the real blockchain as closely
15+
as possible. In cases where **`MultiTest`** does not have a default implementation for the feature,
16+
you can provide your own, using `AppBuilder`'s function listed in **AppBuilder&nbsp;constructor**
17+
column. Names of **`MultiTest`** feature flags required to enable specific functionality are shown
18+
in the column **Feature&nbsp;flag**.
19+
20+
| Feature | Default<br/>implementation | Feature<br/>flag | AppBuilder<br/>constructor | Functionality |
21+
| ------------ | :------------------------: | :--------------: | -------------------------- | -------------------------------------------------- |
22+
| Blocks | **YES** | | `with_block` | Operations on blocks. |
23+
| API | **YES** | | `with_api` | Access to CosmWasm API. |
24+
| Storage | **YES** | | `with_storage` | Access to storage. |
25+
| Bank | **YES** | | `with_bank` | Interactions with **Bank** module. |
26+
| Staking | **YES** | `staking` | `with_staking` | Interactions with **Staking** module. |
27+
| Distribution | **YES** | `staking` | `with_distribution` | Interactions with **Distribution** module. |
28+
| Governance | **NO** | | `with_gov` | Interactions with **Governance** module. |
29+
| Stargate | **NO** | `stargate` | `with_stargate` | Operations using `Stargate` and/or `Any` messages. |
30+
| Wasm | **YES** | | `with_wasm` | Interactions with **Wasm** module. |
31+
| Custom | **NO** | | `new_custom` | Operations using custom module. |
32+
| IBC | **NO** | `stargate` | `with_ibc` | Inter-blockchain communication operations. |
33+
34+
## Feature flags summary
35+
36+
The following table summarizes feature flags supported by **`MultiTest`**.
37+
38+
| Feature flag | Description |
39+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
40+
| `backtrace` | Enables `backtrace` feature in _**anyhow**_ dependency. |
41+
| `staking` | Enables `staking` feature in _**cosmwasm-std**_ dependency and enables staking/distribution functionality in **`MultiTest`** library. |
42+
| `stargate` | Enables `stargate` feature in _**cosmwasm-std**_ dependency and enables stargate/IBC functionality in **`MultiTest`** library. |
43+
| `cosmwasm_1_1` | Enables `cosmwasm_1_1` feature in _**cosmwasm-std**_ dependency. |
44+
| `cosmwasm_1_2` | Enables `cosmwasm_1_2` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_1` feature in **`MultiTest`** library. |
45+
| `cosmwasm_1_3` | Enables `cosmwasm_1_3` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_2` feature in **`MultiTest`** library. |
46+
| `cosmwasm_1_4` | Enables `cosmwasm_1_4` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_3` feature in **`MultiTest`** library. |
47+
| `cosmwasm_2_0` | Enables `cosmwasm_2_0` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_4` feature in **`MultiTest`** library. |
48+
49+
## Starting point
50+
51+
Usually, a good starting point when using **`MultiTest`** is the following dependency configuration
52+
in **Cargo.toml** file:
53+
54+
```toml filename="Cargo.toml" copy
55+
[dependencies]
56+
cosmwasm-std = "2"
57+
58+
[dev-dependencies]
59+
cw-multi-test = { version = "2", features = ["staking", "stargate", "cosmwasm_2_0"] }
60+
```
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-examples
3+
title: Examples
4+
description: Examples of using CW-Multi-Test
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/testing
7+
---
8+
9+
# Examples
10+
11+
To use **cw-multi-test**, you need to understand a few key concepts:
12+
13+
- **App**: Represents the simulated blockchain application, tracking block height and time. You can modify the environment to simulate multiple blocks, using methods like `app.update_block(next_block)`.
14+
- **Mocking Contracts**: You must mock or wrap contracts using **ContractWrapper** to test them within the multi-test environment.
15+
16+
## Example: setting up and testing with cw-multi-test
17+
18+
### Step 1: create a mock app
19+
20+
```rust
21+
fn mock_app() -> App {
22+
let env = mock_env();
23+
let api = Box::new(MockApi::default());
24+
let bank = BankKeeper::new();
25+
App::new(api, env.block, bank, Box::new(MockStorage::new()))
26+
}
27+
```
28+
29+
### Step 2: mock and wrap contracts
30+
31+
```rust
32+
pub fn contract_counter() -> Box<dyn Contract<Empty>> {
33+
let contract = ContractWrapper::new(
34+
execute,
35+
instantiate,
36+
query,
37+
);
38+
Box::new(contract)
39+
}
40+
```
41+
42+
### Step 3: store and instantiate the contract
43+
44+
```rust
45+
let contract_code_id = router.store_code(contract_counter());
46+
let mocked_contract_addr = router
47+
.instantiate_contract(contract_code_id, owner.clone(), &init_msg, &[], "counter", None)
48+
.unwrap();
49+
```
50+
51+
### Step 4: execute and query the contract
52+
53+
```rust
54+
let msg = ExecuteMsg::Increment {};
55+
let _ = router.execute_contract(
56+
owner.clone(),
57+
mocked_contract_addr.clone(),
58+
&msg,
59+
&[],
60+
).unwrap();
61+
62+
let config_msg = QueryMsg::Count {};
63+
let count_response: CountResponse = router
64+
.wrap()
65+
.query_wasm_smart(mocked_contract_addr.clone(), &config_msg)
66+
.unwrap();
67+
assert_eq!(count_response.count, 1);
68+
```
69+
70+
## Mocking third-party contracts
71+
72+
Mocking third-party contracts, such as those from protocols like Terraswap or Anchor, can be challenging since these protocols often don't include the contract code in their Rust packages. However, you can create a thin mock of the service you interact with by implementing only the functions and queries you need.
73+
74+
### Example
75+
76+
```rust
77+
pub fn contract_ping_pong_mock() -> Box<dyn Contract<Empty>> {
78+
let contract = ContractWrapper::new(
79+
|deps, _, info, msg: MockExecuteMsg| -> StdResult<Response> {
80+
match msg {
81+
MockExecuteMsg::Receive(Cw20ReceiveMsg { sender: _, amount: _, msg }) => {
82+
let received: PingMsg = from_binary(&msg)?;
83+
Ok(Response::new()
84+
.add_attribute("action", "pong")
85+
.set_data(to_binary(&received.payload)?))
86+
}
87+
}
88+
},
89+
|_, _, msg: MockQueryMsg| -> StdResult<Binary> {
90+
match msg {
91+
MockQueryMsg::Pair {} => Ok(to_binary(&mock_pair_info())?),
92+
}
93+
},
94+
);
95+
Box::new(contract)
96+
}
97+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
objectID: developers_cosm_wasm_smart-contracts_litmus
3+
title: Litmus
4+
description: Overview of the Litmus package for testing and benchmarking
5+
parentSection: Smart Contracts
6+
parentSectionPath: /developers/smart-contracts/introduction
7+
---
8+
9+
### Litmus - Gas estimation & performance testing
10+
11+
**Litmus** is a powerful testing tool designed to assist developers in optimizing gas usage and testing the performance of smart contracts on the Archway network. It consists of two key projects:
12+
13+
1. **Archway Test Tube**: A wrapper for the Archway blockchain that allows native Rust tests for smart contracts without needing to spin up a full node. It offers real-chain functionality, supporting features like rewards and callbacks.
14+
15+
2. **Ecometer**: A performance testing wrapper for Archway Test Tube that benchmarks transactions and generates gas consumption graphs to help you monitor and optimize your contract's gas efficiency.
16+
17+
For more information on how to use Litmus in your development workflow, including setup instructions and detailed use cases, visit the [Litmus Documentation](/developers/developer-tools/litmus).

middleware/redirects.global.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const redirects: Record<string, string> = {
6666
'/developers/developer-tools/daemon': '/developers/developer-tools/archwayd',
6767

6868
'/developers/frameworks/sylvia': '/developers/resources/frameworks/sylvia',
69+
70+
'/developers/smart-contracts/testing': '/developers/smart-contracts/testing/testing',
6971
};
7072

7173
export default defineNuxtRouteMiddleware(to => {

0 commit comments

Comments
 (0)