This site requires Javascript to be enabled.

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

#[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:

RUST_BACKTRACE=1 cargo test

After compilation, you should see output similar to:

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

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

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.