Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Important: If you're upgrading snforge to version 0.48.0 or later, please read the 0.48.0 Migration Guide.

mock_call

Cheatcodes mocking contract entry point calls:

mock_call

fn mock_call<T, impl TSerde: serde::Serde<T>, impl TDestruct: Destruct<T>>( contract_address: ContractAddress, function_selector: felt252, ret_data: T, n_times: u32 )

Mocks contract call to a function_selector of a contract at the given address, for n_times first calls that are made to the contract. A call to function function_selector will return data provided in ret_data argument. An address with no contract can be mocked as well. An entrypoint that is not present on the deployed contract is also possible to mock. Note that the function is not meant for mocking internal calls - it works only for contract entry points.

start_mock_call

fn start_mock_call<T, impl TSerde: serde::Serde<T>, impl TDestruct: Destruct<T>>( contract_address: ContractAddress, function_selector: felt252, ret_data: T )

Mocks contract call to a function_selector of a contract at the given address, indefinitely. See mock_call for comprehensive definition of how it can be used.

stop_mock_call

fn stop_mock_call(contract_address: ContractAddress, function_selector: felt252)

Cancels the mock_call / start_mock_call for the function function_selector of a contract at the given address.

Example

Let's consider a contract which simulates a shopping cart. It has a function get_products that returns a list of products in the cart.

#[derive(Copy, Debug, Drop, Serde, starknet::Store)]
pub struct Product {
    pub name: felt252,
    pub price: u64,
    pub quantity: u64,
}

#[starknet::interface]
pub trait IShoppingCart<TContractState> {
    fn get_products(self: @TContractState) -> Array<Product>;
}

#[starknet::contract]
pub mod ShoppingCart {
    use starknet::storage::{MutableVecTrait, StoragePointerReadAccess, Vec, VecTrait};
    use super::Product;

    #[storage]
    struct Storage {
        products: Vec<Product>,
    }

    #[constructor]
    fn constructor(ref self: ContractState, initial_products: Array<Product>) {
        for product in initial_products {
            self.products.push(product);
        }
    }

    #[abi(embed_v0)]
    impl ShoppingCartImpl of super::IShoppingCart<ContractState> {
        fn get_products(self: @ContractState) -> Array<Product> {
            let mut products = array![];
            for i in 0..self.products.len() {
                products.append(self.products.at(i).read());
            }
            products
        }
    }
}

Test code:

use cheatcodes_reference::mock_call_example::{
    IShoppingCartDispatcher, IShoppingCartDispatcherTrait, Product,
};
use snforge_std::{ContractClassTrait, DeclareResultTrait, declare, start_mock_call, stop_mock_call};

#[test]
fn test_mock_call() {
    // 1. Create calldata for the contract deployment
    let mut calldata = ArrayTrait::new();

    // 2. Serialize initial products (in this case an empty array)
    let initial_products: Array<Product> = array![];
    initial_products.serialize(ref calldata);

    // 3. Declare and deploy the ShoppingCart contract
    let contract = declare("ShoppingCart").unwrap().contract_class();
    let (contract_address, _) = contract.deploy(@calldata).unwrap();

    // 4. Create an instance of the dispatcher
    let dispatcher = IShoppingCartDispatcher { contract_address };

    // 5. Mock the returned value of the `get_products` function
    let mock_products = array![
        Product { name: 'Banana', price: 2, quantity: 5 },
        Product { name: 'Apple', price: 3, quantity: 10 },
    ];

    // 6. Start the mock call with the mocked products
    start_mock_call(contract_address, selector!("get_products"), mock_products);

    // 7. Call the `get_products` function through the dispatcher
    let retrieved_products: Array<Product> = dispatcher.get_products();

    // 8. Verify the retrieved products
    let product_0 = retrieved_products.at(0);
    assert(*product_0.name == 'Banana', 'product_0.name');
    assert(*product_0.price == 2, 'product_0.price');
    assert(*product_0.quantity == 5, 'product_0.quantity');

    let product_1 = retrieved_products.at(1);
    assert(*product_1.name == 'Apple', 'product_1.name');
    assert(*product_1.price == 3, 'product_1.price');
    assert(*product_1.quantity == 10, 'product_1.quantity');

    // 9. Stop the mock call
    stop_mock_call(contract_address, selector!("get_products"));
}

Let's run the test:

$ snforge test test_mock_call
Output:
Collected 1 test(s) from cheatcodes_reference package
Running 0 test(s) from src/
Running 1 test(s) from tests/
[PASS] cheatcodes_reference_integrationtest::test_mock_call::test_mock_call ([..])
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out