Skip to content

Commit

Permalink
Addition of new testing content to the smart contracts section along …
Browse files Browse the repository at this point in the history
…with the addition of an overview of the Litmus tool (#540)
  • Loading branch information
emperorjm authored Oct 18, 2024
1 parent 6be364e commit 9abb135
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 0 deletions.
105 changes: 105 additions & 0 deletions content/2.developers/3.smart-contracts/12.testing/1.testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
objectID: developers_cosm_wasm_smart-contracts_testing
title: Testing
description: Provides an overview of the testing tools and techniques available to developers
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/introduction
---

# Testing

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**.

## Unit testing

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.

### Writing unit tests

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.

#### Example

```rust
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{from_binary, Uint128};

#[test]
fn test_transfer_funds_success() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("sender", &[]);

let msg = ExecuteMsg::Transfer {
recipient: "recipient".to_string(),
amount: Uint128::new(100),
};
let res = execute(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.messages.len(), 1);
}
}
```

### Running unit tests

To run your unit tests, execute the following command in your terminal:

```bash
RUST_BACKTRACE=1 cargo test
```

After compilation, you should see output similar to:

```text
running 15 tests
test coin_helpers::test::assert_sent_sufficient_coin_works ... ok
test tests::test_module::proper_init_no_fees ... ok
...
test result: ok. 15 passed; 0 failed; 0 ignored; finished in 0.00s
```

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).

### Mocking

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.

#### Example

```rust
fn mock_dependencies() -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
let custom_querier = MockQuerier::new(&[]);
OwnedDeps {
storage: MockStorage::new(),
api: MockApi::default(),
querier: custom_querier,
}
}
```

### Best practices for unit testing

- **Organize Tests**: Group tests by functionality and place them in separate modules or files.
- **Descriptive Naming**: Use descriptive names for tests, such as `test_transfer_funds_success` or `test_invalid_name_rejection`.
- **Focus on Simplicity**: Write clear, concise tests that focus on individual components of the smart contract.
- **Test Edge Cases**: Ensure your smart contract can handle various scenarios by testing edge cases and failure conditions.
- **Independence**: Keep tests independent so that the failure of one test does not impact others.

## Integration testing with cw-multi-test

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.

We've created an entire section covering cw-multi-test that can be viewed [here](/developers/smart-contracts/testing/cw-multi-test/introduction).

## Best practices for integration testing

- **Mock Everything**: Ensure that all contracts and services your contract interacts with are mocked out.
- **Reuse Mocks**: Define reusable functions for wrapping and mocking contracts to simplify your test code.
- **Test Scenarios Thoroughly**: Test a variety of scenarios, including edge cases, to ensure your contracts behave correctly in all situations.

## Conclusion

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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-introduction
title: Introduction
description: An introduction to CW-Multi-Test
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/testing
---

# MultiTest

**`MultiTest`** is a testing tool designed to facilitate multi-contract interactions within the
CosmWasm ecosystem. Its primary focus is on providing developers with a robust framework for
off-chain testing of complex smart contract interactions and operations involving various Cosmos
modules.

::alert{variant="info"}
**`MultiTest`** is a blockchain **SIMULATOR**, allowing tested smart contracts to interact as if
they were operating on a real blockchain.
::

The most valuable advantages of using **`MultiTest`** is that it allows for testing and debugging
smart contracts with access to the Rust source code and eliminates the need to run a complete
blockchain node to begin designing the functionality of the contract. Additionally, **`MultiTest`**
enables the execution of tests significantly faster than on a real blockchain, as it bypasses the
overhead associated with network consensus and block production. This results in a more efficient
development cycle, allowing for quicker iterations and faster identification of issues, even before
the smart contract is deployed on the blockchain.

While **`MultiTest`** is a blockchain **SIMULATOR**, it may happen, that the behavior of the real
blockchain might slightly differ in some edge cases.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-installation
title: Installation
description: How to install CW-Multi-Test
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/testing
---

# Installation

**`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).

## Usage

To use **`MultiTest`** in your project, simply add it as a **development dependency** to
**Cargo.toml** file:

```toml filename="Cargo.toml" copy
[dev-dependencies]
cw-multi-test = "2"
```

<br />

::alert{variant="info"}
**`MultiTest`** is a **TESTING** library and should **ALWAYS** be added to your project as a
**DEVELOPMENT DEPENDENCY** in section **`[dev-dependencies]`** of the **Cargo.toml** file.
::

<br />

::alert{variant="info"}
**`MultiTest`** **IS NOT** designed to be used in production code on a real-life blockchain.
::

## Prerequisities

### Rust and Cargo

The only prerequisite to test smart contracts using **`MultiTest`** is having [Rust and Cargo](https://www.rust-lang.org/tools/install) installed.

::alert{variant="info"}
We recommend installing Rust using the official [rustup installer](https://rustup.rs). This makes it easy to stay on
the most recent version of Rust and Cargo.
::

### Tarpaulin and cargo-nextest

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.

Installing **Tarpaulin**:

```shell copy filename="TERMINAL"
cargo install cargo-tarpaulin
```

Installing **cargo-nextest**:

```shell copy filename="TERMINAL"
cargo install cargo-nextest
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-features
title: Features
description: Features of CW-Multi-Test
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/testing
---

# Features

All **`MultiTest`** features are listed in the table below.

The **Default&nbsp;implementation** column indicates whether the feature has a default
implementation in **`MultiTest`** that simulates the functionality of the real blockchain as closely
as possible. In cases where **`MultiTest`** does not have a default implementation for the feature,
you can provide your own, using `AppBuilder`'s function listed in **AppBuilder&nbsp;constructor**
column. Names of **`MultiTest`** feature flags required to enable specific functionality are shown
in the column **Feature&nbsp;flag**.

| Feature | Default<br/>implementation | Feature<br/>flag | AppBuilder<br/>constructor | Functionality |
| ------------ | :------------------------: | :--------------: | -------------------------- | -------------------------------------------------- |
| Blocks | **YES** | | `with_block` | Operations on blocks. |
| API | **YES** | | `with_api` | Access to CosmWasm API. |
| Storage | **YES** | | `with_storage` | Access to storage. |
| Bank | **YES** | | `with_bank` | Interactions with **Bank** module. |
| Staking | **YES** | `staking` | `with_staking` | Interactions with **Staking** module. |
| Distribution | **YES** | `staking` | `with_distribution` | Interactions with **Distribution** module. |
| Governance | **NO** | | `with_gov` | Interactions with **Governance** module. |
| Stargate | **NO** | `stargate` | `with_stargate` | Operations using `Stargate` and/or `Any` messages. |
| Wasm | **YES** | | `with_wasm` | Interactions with **Wasm** module. |
| Custom | **NO** | | `new_custom` | Operations using custom module. |
| IBC | **NO** | `stargate` | `with_ibc` | Inter-blockchain communication operations. |

## Feature flags summary

The following table summarizes feature flags supported by **`MultiTest`**.

| Feature flag | Description |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `backtrace` | Enables `backtrace` feature in _**anyhow**_ dependency. |
| `staking` | Enables `staking` feature in _**cosmwasm-std**_ dependency and enables staking/distribution functionality in **`MultiTest`** library. |
| `stargate` | Enables `stargate` feature in _**cosmwasm-std**_ dependency and enables stargate/IBC functionality in **`MultiTest`** library. |
| `cosmwasm_1_1` | Enables `cosmwasm_1_1` feature in _**cosmwasm-std**_ dependency. |
| `cosmwasm_1_2` | Enables `cosmwasm_1_2` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_1` feature in **`MultiTest`** library. |
| `cosmwasm_1_3` | Enables `cosmwasm_1_3` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_2` feature in **`MultiTest`** library. |
| `cosmwasm_1_4` | Enables `cosmwasm_1_4` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_3` feature in **`MultiTest`** library. |
| `cosmwasm_2_0` | Enables `cosmwasm_2_0` feature in _**cosmwasm-std**_ dependency and additionally `cosmwasm_1_4` feature in **`MultiTest`** library. |

## Starting point

Usually, a good starting point when using **`MultiTest`** is the following dependency configuration
in **Cargo.toml** file:

```toml filename="Cargo.toml" copy
[dependencies]
cosmwasm-std = "2"

[dev-dependencies]
cw-multi-test = { version = "2", features = ["staking", "stargate", "cosmwasm_2_0"] }
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
objectID: developers_cosm_wasm_smart-contracts_testing-cw_multi_test-examples
title: Examples
description: Examples of using CW-Multi-Test
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/testing
---

# Examples

To use **cw-multi-test**, you need to understand a few key concepts:

- **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)`.
- **Mocking Contracts**: You must mock or wrap contracts using **ContractWrapper** to test them within the multi-test environment.

## Example: setting up and testing with cw-multi-test

### Step 1: create a mock app

```rust
fn mock_app() -> App {
let env = mock_env();
let api = Box::new(MockApi::default());
let bank = BankKeeper::new();
App::new(api, env.block, bank, Box::new(MockStorage::new()))
}
```

### Step 2: mock and wrap contracts

```rust
pub fn contract_counter() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
execute,
instantiate,
query,
);
Box::new(contract)
}
```

### Step 3: store and instantiate the contract

```rust
let contract_code_id = router.store_code(contract_counter());
let mocked_contract_addr = router
.instantiate_contract(contract_code_id, owner.clone(), &init_msg, &[], "counter", None)
.unwrap();
```

### Step 4: execute and query the contract

```rust
let msg = ExecuteMsg::Increment {};
let _ = router.execute_contract(
owner.clone(),
mocked_contract_addr.clone(),
&msg,
&[],
).unwrap();

let config_msg = QueryMsg::Count {};
let count_response: CountResponse = router
.wrap()
.query_wasm_smart(mocked_contract_addr.clone(), &config_msg)
.unwrap();
assert_eq!(count_response.count, 1);
```

## Mocking third-party contracts

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.

### Example

```rust
pub fn contract_ping_pong_mock() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
|deps, _, info, msg: MockExecuteMsg| -> StdResult<Response> {
match msg {
MockExecuteMsg::Receive(Cw20ReceiveMsg { sender: _, amount: _, msg }) => {
let received: PingMsg = from_binary(&msg)?;
Ok(Response::new()
.add_attribute("action", "pong")
.set_data(to_binary(&received.payload)?))
}
}
},
|_, _, msg: MockQueryMsg| -> StdResult<Binary> {
match msg {
MockQueryMsg::Pair {} => Ok(to_binary(&mock_pair_info())?),
}
},
);
Box::new(contract)
}
```
17 changes: 17 additions & 0 deletions content/2.developers/3.smart-contracts/12.testing/3.litmus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
objectID: developers_cosm_wasm_smart-contracts_litmus
title: Litmus
description: Overview of the Litmus package for testing and benchmarking
parentSection: Smart Contracts
parentSectionPath: /developers/smart-contracts/introduction
---

### Litmus - Gas estimation & performance testing

**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:

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.

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.

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).
2 changes: 2 additions & 0 deletions middleware/redirects.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const redirects: Record<string, string> = {
'/developers/developer-tools/daemon': '/developers/developer-tools/archwayd',

'/developers/frameworks/sylvia': '/developers/resources/frameworks/sylvia',

'/developers/smart-contracts/testing': '/developers/smart-contracts/testing/testing',
};

export default defineNuxtRouteMiddleware(to => {
Expand Down

0 comments on commit 9abb135

Please sign in to comment.