Jitterbug
Jitterbug

Reputation: 312

Mocktopus alternative for mocking functions

Our code is heavily using Mocktopus but we're planning to use the rust stable now.

This is a scenario that I can't resolve:

use mocktopus::*;
use mocktopus::mocking::{Mockable, MockResult};
use crate::prod::Limiter;

pub mod helper {
    #[cfg(test)]
    use mocktopus::macros::mockable;

    #[cfg_attr(test, mockable)]
    pub fn min() -> u64 {
        0
    }

    #[cfg_attr(test, mockable)]
    pub fn max() -> u64 {
        10
    }

    pub fn one() -> u64 {
        1
    }
}

pub mod prod {
    pub struct Limiter{
        min: u64,
        max: u64
    }

    impl Limiter {
        pub fn new() -> Self {
            Limiter {
                min: 0,
                max: 0,
            }
        }
        pub fn set_min(&mut self, allow_zero:bool){
            self.min = if allow_zero {
                super::helper::min()

            } else { super::helper::one() };

        }

        pub fn set_max(&mut self,allow_max_of_type:bool) {
            self.max = if allow_max_of_type {
                u64::MAX
            } else { super::helper::max() };
        }

        pub fn get_min(&self) -> u64 {
            self.min
        }

        pub fn get_max(&self) -> u64 {
            self.max
        }
    }
}

(note: this is NOT the actual code; just a VERY simple view of what our code structure looks like)

For Mocktopus I can test Limiter like so:


#[test]
fn test_get_min() {
    let mut limiter = Limiter::new();
    helper::min.mock_safe(|| MockResult::Return(2));

    limiter.set_min(true);
    assert_eq!(limiter.get_min(), 2);
}

But I can't find any libraries similar to this.

I tried mockall, but it requires me to wrap the structure inside mock! macro. For context: I can't simply do that, since the structure (the Limiter) is created through another macro.



Here's a project(Interlay which uses Parity's substrate) that showcases better my dilemma:

To test register_vault() method:

 pub fn register_vault(
            origin: OriginFor<T>,
            currency_pair: DefaultVaultCurrencyPair<T>,
            #[pallet::compact] collateral: BalanceOf<T>,
        ) -> DispatchResultWithPostInfo {
            ...
            Self::_register_vault(vault_id, collateral)?;
    ...
 }

where _register_vault() calls the get_minimum_collateral_vault() function:

pub fn _register_vault(vault_id: DefaultVaultId<T>, collateral: BalanceOf<T>) -> DispatchResult {
    ...
    ensure!(amount.ge(&Self::get_minimum_collateral_vault(collateral_currency))?,
            Error::<T>::InsufficientVaultCollateralAmount);
    ...

}

The function get_minimum_collateral_vault is set to mockable:

#[cfg_attr(test, mockable)]
impl<T: Config> Pallet<T> {
...
fn get_minimum_collateral_vault(currency_id: CurrencyId<T>) -> Amount<T> {
        let amount = MinimumCollateralVault::<T>::get(currency_id);
        Amount::new(amount, currency_id)
    }
}

Inside this test case mocks the function get_minimum_collateral_vault to be able to test register_vault:

VaultRegistry::get_minimum_collateral_vault
            .mock_safe(move |currency_id| MockResult::Return(Amount::new(200, currency_id)));
...
let result = VaultRegistry::register_vault(origin, id.currencies.clone(), collateral);
assert_err!(result, TestError::InsufficientVaultCollateralAmount);

Upvotes: 0

Views: 87

Answers (0)

Related Questions