diff --git a/content/2.developers/3.smart-contracts/12.testing/1.testing.md b/content/2.developers/3.smart-contracts/12.testing/1.testing.md new file mode 100644 index 00000000..45aaf351 --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/1.testing.md @@ -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 { + 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. \ No newline at end of file diff --git a/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/1.introduction.md b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/1.introduction.md new file mode 100644 index 00000000..a494bcc9 --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/1.introduction.md @@ -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. \ No newline at end of file diff --git a/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/2.installation.md b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/2.installation.md new file mode 100644 index 00000000..0ccf06cd --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/2.installation.md @@ -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" +``` + +
+ +::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. +:: + +
+ +::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 +``` \ No newline at end of file diff --git a/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/3.features.md b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/3.features.md new file mode 100644 index 00000000..8f61d948 --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/3.features.md @@ -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 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 constructor** +column. Names of **`MultiTest`** feature flags required to enable specific functionality are shown +in the column **Feature flag**. + +| Feature | Default
implementation | Feature
flag | AppBuilder
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"] } +``` \ No newline at end of file diff --git a/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/4.examples.md b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/4.examples.md new file mode 100644 index 00000000..ddfada19 --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/2.cw-multi-test/4.examples.md @@ -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> { + 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> { + let contract = ContractWrapper::new( + |deps, _, info, msg: MockExecuteMsg| -> StdResult { + 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 { + match msg { + MockQueryMsg::Pair {} => Ok(to_binary(&mock_pair_info())?), + } + }, + ); + Box::new(contract) +} +``` \ No newline at end of file diff --git a/content/2.developers/3.smart-contracts/12.testing/3.litmus.md b/content/2.developers/3.smart-contracts/12.testing/3.litmus.md new file mode 100644 index 00000000..c5254d42 --- /dev/null +++ b/content/2.developers/3.smart-contracts/12.testing/3.litmus.md @@ -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). \ No newline at end of file diff --git a/middleware/redirects.global.ts b/middleware/redirects.global.ts index 69cbeb71..5d4ba53a 100644 --- a/middleware/redirects.global.ts +++ b/middleware/redirects.global.ts @@ -66,6 +66,8 @@ const redirects: Record = { '/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 => {