IC-Mockery is a testing and mocking framework built for Internet Computer (IC) canister development in Rust.
It simplifies mocking of async method calls by transforming them into HTTP outcalls—enabling full test coverage with PocketIC while keeping production code untouched.
Features
- Mock Async HTTP Calls: Seamlessly mock any
async fn -> Result<_, _>method. - Built on PocketIC: Simulate canister behavior locally.
- Proc Macro Powered: Use
#[mock_async_calls]to transform methods. - Fluent API: Easy-to-read mocks and call chains.
Flexible Error Handling: Works with any error type that implementsFrom<String>, including custom error enums and(RejectionCode, String).
Installation
# In Cargo.toml
[dependencies]
ic-mockery = { git = "https://github.com/ic-mockery/ic-mockery" }
ic-mockery-macro = { git = "https://github.com/ic-mockery/ic-mockery" }
Usage Example
A full example using #[mock_async_calls] and AsyncMocker.
1. Define Types
use candid::{CandidType, Deserialize};
use serde::{Serialize};
#[derive(CandidType, Deserialize, Serialize, Clone)]
pub struct GreetRequest {
pub name: String,
}
#[derive(CandidType, Deserialize, Serialize, Debug, PartialEq)]
pub struct GreetResponse {
pub message: String,
pub status: Status,
}
#[derive(CandidType, Deserialize, Serialize, Debug, PartialEq)]
pub enum Status {
Success,
Error,
}
2. Implement Your Canister Logic
Use the #[mock_async_calls] macro to make your service methods mockable.
use ic_mockery_macro::mock_async_calls;
pub struct HelloService;
#[mock_async_calls]
impl HelloService {
pub async fn greet(req: GreetRequest) -> Result<GreetResponse, String> {
// This method will be transformed to use HTTP outcalls under the hood
Ok(GreetResponse {
message: format!("Hello, {}!", req.name),
status: Status::Success,
})
}
pub async fn prepare_greet(req: GreetRequest) -> Result<(), String> {
// Also mockable
Ok(())
}
}
3. Expose Canister API
Use the service from your actual canister entrypoint.
#[ic_cdk::update]
async fn greet(req: GreetRequest) -> GreetResponse {
HelloService::prepare_greet(req.clone()).await.unwrap();
HelloService::greet(req).await.unwrap()
}
5. Test Cases
Mock in action
#[test]
fn test_greet_functionality() {
// Create a PocketIC test environment
let pic = PocketIcBuilder::new().with_application_subnet().build();
let canister = pic.create_canister();
// Add cycles and install the canister (replace `wasm_bytes` with actual WASM)
pic.add_cycles(canister, 2_000_000_000_000);
pic.install_canister(canister, wasm_bytes, vec![], None);
// Set up mocks and run the test
let result = AsyncMocker::new(&pic)
.call(
canister,
Principal::anonymous(),
"greet",
GreetRequest {
name: "Wizard".into(),
},
)
.mock("prepare_greet", |_| to_value(()).unwrap())
.mock("greet", |args| {
let name = args["args"][0]["name"].as_str().unwrap();
to_value(GreetResponse {
message: format!("Hello, {}!", name),
status: Status::Success,
}).unwrap()
})
.execute::<GreetResponse>()
.unwrap();
assert_eq!(result.message, "Hello, Wizard!");
assert_eq!(result.status, Status::Success);
}
Failure simulation
#[test]
fn test_greet_failure() {
// Step 1: Set up a local PocketIC environment
let pic = PocketIcBuilder::new().with_application_subnet().build();
let canister = pic.create_canister();
// Step 2: Fund and install the canister (replace `wasm_bytes` with your compiled WASM)
pic.add_cycles(canister, 2_000_000_000_000);
pic.install_canister(canister, wasm_bytes, vec![], None);
// Step 3: Call the `greet` method, mocking internal calls
let result = AsyncMocker::new(&pic)
.call(
canister,
Principal::anonymous(),
"greet",
GreetRequest {
name: "Error".into(), // This input is irelevant since we are simulating failure anyway
},
)
// Mock `prepare_greet` to succeed (returns `Ok(())`)
.mock("prepare_greet", |_| to_value(()).unwrap())
// Mock `greet` to fail with a specific error
.mock_fail("greet", "Invalid name provided")
.execute::<GreetResponse>();
// Step 4: Verify that the failure is properly handled
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Invalid name provided");
}
Final Thoughts
The goal of IC-Mockery is to make testing async IC canisters simple, safe, and fully mockable with minimal boilerplate, full type safety.
→ github.com/ic-mockery/ic-mockery — feedback & PRs welcome!