3

In my past experience of testing C code, the function stub is pretty much mandatory. In my safety-critical line of work, I am usually required to test everything - even abstractive functions which simply call a build-specific implementation argument for argument, return for return.

I have searched high and low for ways to, in Rust, stub functions external to the module under test, but cannot find a solution.

There are several libraries for trait-mocking, but what little I have read about this topic suggests that it is not what I am looking for.

The other suggestion is that functions I test which call external functions have those functions passed in, allowing the test simply to pass the desired pseudostub function. This seems to be very inflexible in terms of the data passing in and out of the pseudostub and causes one's code to be polluted with function-reference arguments at every level - very undesirable when the function under test would never call anything except one operational function or the stub. You are writing operational code to fit in with the limitations of the testing system.

This seems so very basic. Surely there is a way to stub external functions with Rust and Cargo?

Walkingbeard
  • 590
  • 5
  • 15
  • Does this answer your question? [How to mock external dependencies in tests?](https://stackoverflow.com/questions/51919079/how-to-mock-external-dependencies-in-tests) – E_net4 Mar 12 '21 at 16:30
  • _"Surely there is a way to stub external functions with Rust and Cargo?"_ The short answer is "not without using some macro magic, which some crate out there might provide but YMMV". _"causes one's code to be polluted with function-reference arguments at every level"_ not entirely true if you create a type alias to the tested module with the standard implementation. – E_net4 Mar 12 '21 at 16:31
  • Hi, thanks for the replies. I'm not sure I follow the second one. The first one though, is exactly what I want to avoid - making the code more complex just so that it can be tested. – Walkingbeard Mar 13 '21 at 10:54
  • If dependency injection via traits isn't an option, you can make your `use` statements import different things in the test environment with [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html). Its not very flexible, but is that maybe something you want? – kmdreko Mar 14 '21 at 19:08

1 Answers1

2

You can try and use mock crates like mockall, which I think is the more complete out there, but still might need some time getting used to.
Without a mock crate, I would suggest mocking the traits/structs in an other, and then bringing then into scope with the #[cfg(test)] attribute. Of course this would make it mandatory for you to annotate the production use statement with `#[cfg(not(test))]. for example:

If you're using an external struct ExternalStruct from external-crate with a method external_method you would have something like:

file real_code.rs

#[cfg(not(test))]
use external-crate::ExternalStruct;
#[cfg(test)]
use test_mocks::ExternalStruct;

fn my_function() {
   //...
   ExternalTrait::external_method();
}

#[test]
fn test_my_function(){
   my_function();
}

file test_mocks.rs:

pub Struct ExternalStruct {}

impl ExternalStruct {
   pub fn external_method() {
      //desired mock behaviour
   }
}

On running your test the ExternalStruct from the test_mocks will be used and the real dependency otherwise.

Dharman
  • 30,962
  • 25
  • 85
  • 135
vVicente
  • 48
  • 8