How to mock specific methods but not all of them in Rust?

后端 未结 2 1900
野性不改
野性不改 2020-12-06 23:56

I have troubles figuring out unit tests for the methods of the target struct.

I have a method random_number that returns a random value based on the att

2条回答
  •  没有蜡笔的小新
    2020-12-07 00:51

    How to mock specific methods but not all of them in Rust?

    As you have already learned, you cannot replace methods on a type. The only thing you can do is move the methods to a trait and then provide production and test-specific implementations of that trait. How you structure the trait determines the granularity of what you are able to test.

    Trait with a default implementation

    Depending on your use case, you might be able to use a default implementation:

    trait SomeRng {
        fn random_number(&self) -> u64;
    
        fn plus_one(&self) -> u64 {
            self.random_number() + 1
        }
    }
    
    struct RngTest(u64);
    impl SomeRng for RngTest {
        fn random_number(&self) -> u64 {
            self.0
        }
    }
    
    #[test]
    fn plus_one_works() {
        let rng = RngTest(41);
        assert_eq!(rng.plus_one(), 42);
    }
    

    Here, random_number is a required method, but plus_one has a default implementation. Implementing random_number gives you plus_one by default. You could also choose to implement plus_one if you could do it more efficiently.

    What does the real rand crate do?

    The real rand crate uses two traits:

    • Rng

      pub trait Rng: RngCore { /* ... */ }
      

      An automatically-implemented extension trait on RngCore providing high-level generic methods for sampling values and other convenience methods.

    • RngCore

      pub trait RngCore { /* ... */ }
      

      The core of a random number generator.

    This splits the core interesting parts of the implementation from the helper methods. You can then control the core and test the helpers:

    trait SomeRngCore {
        fn random_number(&self) -> u64;
    }
    
    trait SomeRng: SomeRngCore {
        fn plus_one(&self) -> u64 {
            self.random_number() + 1
        }
    }
    
    impl SomeRng for R {}
    
    struct RngTest(u64);
    impl SomeRngCore for RngTest {
        fn random_number(&self) -> u64 {
            self.0
        }
    }
    
    #[test]
    fn plus_one_works() {
        let rng = RngTest(41);
        assert_eq!(rng.plus_one(), 42);
    }
    

提交回复
热议问题