XION
DiscordGithub
  • Welcome to XION
  • XION'S Core
    • Concepts
      • Generalized Chain Abstraction
      • Intro to Account Abstraction
      • XION's Meta Accounts
      • Meta Accounts Design
      • Architecture & Tech Glossary
      • Use Cases
  • developers
    • Xion Quick Start
      • Zero to Dapp in 5 Minutes
        • Launch a User Map Dapp on XION in 5 Minutes
        • React Native Mobile Dapp on XION in 5 Minutes
    • Mobile dapp Development on XION
      • Set up your XION Mobile Development Environment
      • Create Mobile Dapp and Integrate Meta Account Authentication
    • Getting Started (Advanced)
      • Set Up Local Environment
        • Setting up your Local Smart Contract Development Environment for XION
          • Setting up your XION Smart Contract Development Environment on Windows (WSL2 + Ubuntu)
        • Set Up an Integrated Development Environment (IDE)
        • Interacting with Xion Chain using Xion Daemon
      • Your First Contract
        • Deploying Your First Smart Contract on XION
      • Gasless UX & Permission Grants
        • Enabling Gasless Transactions with Treasury Contracts
      • Dapp Development
        • Account Abstraction with Gasless Transactions
        • Interact with XION via your Backend Service
    • Re-using Existing Contracts
      • Deployed Contracts on XION
      • Non-Fungible Tokens (NFTs)
      • Fungible Tokens
      • Marketplace
      • Multisig
      • Proxy Contracts
      • Membership Management
      • Governance
      • Valuts
      • SCV Audited Contracts
    • Web3 for Web2 Developers
      • Web2 vs Web3 App Architecture: A Comparison
      • Misconceptions and Misapplied Web2 Patterns
      • Recommended Architecture for Dapps on XION
    • Building for Mainnet
      • Xion Testnet: Your Development Playground
      • Building with Audited & Battle-Tested Contracts
      • Community Engagement: Building Support for Your dApp
      • Deploying to Xion Mainnet
        • Smart Contract Audits: Process, Costs & Support
        • Governance Process to Deploying Smart Contracts to Mainnet
    • Learn & Build
      • Token Factory
        • Creating, Minting, and Interacting with a Token Factory Token
        • Building a React dApp to Interact with Token Factory Tokens
        • Integrating a Token Factory Token in a Smart Contract
      • Websockets
        • WebSockets with Xion: Real-Time Communication
      • Mobile Development
        • Building a React Native Mobile App with Abstraxion (Xion.js)
      • Oracles
        • Creating a Smart Contract with Pyth Oracle Integration
      • Indexers: Optimized Data Retrieval
        • SubQuery
      • Use Cases
        • Building a Per-User Data Storage Dapp
    • Reference and Resources
      • Requesting XION Testnet Tokens
      • Public Endpoints & Resources
      • Block Explorers
      • Governance
        • Deploying Smart Contracts to Mainnet
      • Developer Tools: Abstract
      • IBC Denoms on XION Networks
      • Frequently Asked Questions
      • XION Token Contract Addresses on EVM Chains
  • Nodes & Validators
    • Run a Node
      • System Specifications
      • Build the Xion Daemon
      • Download the Xion Daemon
      • Configure the Xion Daemon
        • app.toml
        • client.toml
        • config.toml
      • Join the XION Network
        • xion-testnet-1
      • Confirm node is running
    • Become a Validator
      • Initial Setup
      • Obtain a XION Wallet Address
      • Obtain Funds
        • Testnet
      • Create Validator
    • IBC Relayers and Tokens
  • Others
    • Resources
Powered by GitBook
On this page
  • Test Initialization
  • Mocking Dependencies, Environment, and Message Information
  • mock_dependencies is used for mocking storage, api, and querier.
  • mock_env is used for mocking blocks, and contract environments.
  • mock_info is used for mocking transaction environments.

Was this helpful?

Edit on GitHub
  1. developers
  2. Reference and Resources
  3. CosmWasm Resources
  4. Tutorials

Testing

Last updated 2 months ago

Was this helpful?

This section provides insights into coding and contract testing practices. It covers various aspects related to cosmwasm smart contracts and offers valuable best practices and tips for effective testing:

1. Testing Query Functions

Learn how to write and execute unit tests for cosmwasm smart contract query functions. Best practices in this area involve testing query functions with a range of parameters to validate their responses. This approach ensures that users can seamlessly interact with the smart contract as intended.

2. Testing Error Handling and Edge Cases

Discover the importance of testing error handling scenarios and edge cases within your contract. This testing ensures that your smart contract behaves correctly when faced with unexpected situations or unusual inputs, thereby enhancing its robustness and reliability.

3. Testing with Custom Mock Dependencies

Explore the concept of creating custom mock dependencies for your tests. These dependencies simulate various scenarios and conditions that may arise during the execution of your smart contract. By doing so, you can verify your contract's behavior under diverse circumstances, ensuring its adaptability and effectiveness.

For comprehensive guidance on conducting unit testing for cosmwasm smart contracts, refer to the following tutorial linked .

By this stage, your code should be successfully compiled, even though you may not have yet confirmed its functionality. While it's possible to deploy the code to the blockchain after every change, this approach can be time-consuming and inefficient. It's also essential to maintain the contract's integrity and ensure thorough testing for future modifications.

#[cfg(test)]
mod tests {
  use super::*;
  use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR};
  use cosmwasm_std::{attr, coins, CosmosMsg};

You can choose whether to house your tests and code within the same file or separate files. An example can be found .

Test Initialization

For each test, it is important to mock specific variables, such as block time and state. Writing a function for easy setup can make this process more manageable

#[test]
fn proper_initialization() {
  let mut deps = mock_dependencies(&[]);
  let msg = InitMsg {
    counter_offer: coins(40, "ETH"),
    expires: 100_000,
  };
  let info = mock_info("creator", &coins(1, "BTC"));
  
  // we can just call .unwrap() to assert this was a success
  
  let res = init(deps.as_mut(), mock_env(), info, msg).unwrap();
  assert_eq!(0, res.messages.len());
  
  // it worked, let's query the state
  
  let res = query_config(deps.as_ref()).unwrap();
  assert_eq!(100_000, res.expires);
  assert_eq!("creator", res.owner.as_str());
  assert_eq!("creator", res.creator.as_str());
  assert_eq!(coins(1, "BTC"), res.collateral);
  assert_eq!(coins(40, "ETH"), res.counter_offer);
}

Mocking Dependencies, Environment, and Message Information

We should enhance three key mocking tools:

/// All external requirements that can be injected for unit tests.
/// It sets the given balance for the contract itself, nothing else

pub fn mock_dependencies(
  contract_balance: &[Coin],
) -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
  let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR);
  OwnedDeps {
    storage: MockStorage::default(),
    api: MockApi::default(),
    querier: MockQuerier::new(&[(&contract_addr, contract_balance)]),
  }
}

mock_dependencies is used for mocking storage, api, and querier.

/// Returns a default enviroment with height, time, chain_id, and contract address.
/// You can submit as is to most contracts, or modify height/time if you want to
/// test for expiration.
///
/// This is intended for use in test code only.

pub fn mock_env() -> Env {
  Env {
    block: BlockInfo {
      height: 12_345,
      time: 1_571_797_419,
      time_nanos: 879305533,
      chain_id: "xion-testnet-2".to_string(),
    },
    contract: ContractInfo {
      address: HumanAddr::from(MOCK_CONTRACT_ADDR),
    },
  }
}

mock_env is used for mocking blocks, and contract environments.

/// Just set sender and sent funds for the message.
/// This is intended for use in test code only.

pub fn mock_info<U: Into<HumanAddr>>(sender: U, sent: &[Coin]) -> MessageInfo {
  MessageInfo {
    sender: sender.into(),
    sent_funds: sent.to_vec(),
  }
}

mock_info is used for mocking transaction environments.

Test Transfer Handler

#[test]
fn transfer() {
  let mut deps = mock_dependencies(&[]);
  let msg = InitMsg {
    counter_offer: coins(40, "ETH"),
    expires: 100_000,
  };
  let info = mock_info("creator", &coins(1, "BTC"));
  
  // we can just call .unwrap() to assert this was a success
  
  let res = init(deps.as_mut(), mock_env(), info, msg).unwrap();
  assert_eq!(0, res.messages.len());
  
  // random cannot transfer
  
  let info = mock_info("anyone", &[]);
  let err = handle_transfer(deps.as_mut(), mock_env(), info, HumanAddr::from("anyone"))
    .unwrap_err();
  match err {
    ContractError::Unauthorized {} => {}
    e => panic!("unexpected error: {}", e),
  }
  
  // owner can transfer
  
  let info = mock_info("creator", &[]);
  let res =
    handle_transfer(deps.as_mut(), mock_env(), info, HumanAddr::from("someone")).unwrap();
  assert_eq!(res.attributes.len(), 2);
  assert_eq!(res.attributes[0], attr("action", "transfer"));
  
  // check updated properly
  
  let res = query_config(deps.as_ref()).unwrap();
  assert_eq!("someone", res.owner.as_str());
  assert_eq!("creator", res.creator.as_str());
}

Test Execute

#[test]
fn execute() {
  let mut deps = mock_dependencies(&[]);
  let amount = coins(40, "ETH");
  let collateral = coins(1, "BTC");
  let expires = 100_000;
  let msg = InitMsg {
    counter_offer: amount.clone(),
    expires: expires,
  };
  let info = mock_info("creator", &collateral);
  // we can just call .unwrap() to assert this was a success
  let _ = init(deps.as_mut(), mock_env(), info, msg).unwrap();
  // set new owner
  let info = mock_info("creator", &[]);
  let _ = handle_transfer(deps.as_mut(), mock_env(), info, HumanAddr::from("owner")).unwrap();
  // random cannot execute
  let info = mock_info("creator", &amount);
  let err = handle_execute(deps.as_mut(), mock_env(), info).unwrap_err();
  match err {
    ContractError::Unauthorized {} => {}
    e => panic!("unexpected error: {}", e),
  }
  // expired cannot execute
  let info = mock_info("owner", &amount);
  let mut env = mock_env();
  env.block.height = 200_000;
  let err = handle_execute(deps.as_mut(), env, info).unwrap_err();
  match err {
    ContractError::OptionExpired { expired } => assert_eq!(expired, expires),
    e => panic!("unexpected error: {}", e),
  }
  // bad counter_offer cannot execute
  let msg_offer = coins(39, "ETH");
  let info = mock_info("owner", &msg_offer);
  let err = handle_execute(deps.as_mut(), mock_env(), info).unwrap_err();
  match err {
    ContractError::CounterOfferMismatch {
      offer,
      counter_offer,
    } => {
      assert_eq!(msg_offer, offer);
      assert_eq!(amount, counter_offer);
    }
    e => panic!("unexpected error: {}", e),
  }
  // proper execution
  let info = mock_info("owner", &amount);
  let res = handle_execute(deps.as_mut(), mock_env(), info).unwrap();
  assert_eq!(res.messages.len(), 2);
  assert_eq!(
    res.messages[0],
    CosmosMsg::Bank(BankMsg::Send {
      from_address: MOCK_CONTRACT_ADDR.into(),
      to_address: "creator".into(),
      amount,
    })
  );
  assert_eq!(
    res.messages[1],
    CosmosMsg::Bank(BankMsg::Send {
      from_address: MOCK_CONTRACT_ADDR.into(),
      to_address: "owner".into(),
      amount: collateral,
    })
  );
  // check deleted
  let _ = query_config(deps.as_ref()).unwrap_err();
}

Now you are prepared to run the tests:

cargo test

Excellent, you've established a foundational test environment initializer. This one is relatively straightforward, allowing you to pass variables to the function and implement diverse adjustments. For additional customization options, consider exploring .

here
here
cosmwasm-plus