Any recommendation of mocking/test libraries that help with stubbing?

Hi guys,

I've recently been familiarising myself with mockall to aid with unit testing. There are some instances, however, where mockall doesn't feel like a good fit. An example would a trait that has many functions that modify (or at least can been seen to) the same dataset; it's much easier to simply stub the dataset and view its final state to verify the result of my testing. This produces tests that are terser and less fragile and implementation specific (as they don't rely upon which specific combination and order of functions I call - only the final result).

My questions are:

  1. Is there a way to do this elegantly within mockall?
  2. If not, is there another library I should be looking at that helps produce and probe stubs? Does it work alongside mockall or is a different approach to testing altogether?
  3. Or perhaps someone has a general opinion on testing that's worth sharing?

Many thanks

1 Like

My experience is that in many cases, you can test things without actually using any mocks perfectly fine. For example, to test a web server, you can start it on port zero to have it get a random free port, then connection to it over a localhost-TCP connection. You can create temporary files for things that read files.

I know that some people don't like doing this kind of thing, but I think it works really well for the things I've needed it for.

Mocking frameworks are really powerful tools, but in my experience they tend to hide the real problem - your code is hard to test and needs refactoring.

For a test case like yours where it is mostly functional (data goes in, do processing, result comes out, no noticeable side-effects), you shouldn't need much more than the assert_eq!() macro to make sure the dataset is processed in the way you expect.

#[test]
fn my_complicated_test() {
  let initial_state = ...;
  let expected = ...;

  let actual = process_dataset(initial_state);

  assert_eq!(actual, expected);
}

If constructing your initial_state and expected values is difficult then I'd interpret that as the code telling me there is an architecture/usability issue... I would look at my code to see why constructing a value manually is difficult, then address it by either splitting the object into multiple parts, moving dependencies around (or removing them entirely). As a last resort you can create convenience functions/macros or deserialize a known version.

If your process_dataset() function takes some extra object besides the dataset, or if it normally has side-effects (e.g. writing to disk or making a bank transaction) then you can use dependency injection techniques (e.g. replace concrete type with trait) to let you pass in your own implementation for testing purposes. If that trait has lots of methods and writing the mock object manually is painful, then that's usually an indicator your trait is not abstract enough or your code-under-test is doing too much.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.