I am trying to write a unit test for my main function where I mock the "process_csv_file" function and return some hardcoded value. Here is my code right now:
main.rs
mod helpers {
// #[mockall::automock()]
pub(super) mod csv_processor;
}
use std::error::Error;
use crate::helpers::csv_processor::process_csv_file;
fn main() -> Result<(), Box<dyn Error>> {
let hardcoded_file_path = "./csv/example_file.csv".to_string();
match process_csv_file(hardcoded_file_path) {
Ok(result) => println!("CSV file processed successfully: {}", result),
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}
#[cfg(test)]
mod main_unit_tests {
// mock "process_csv_file" function call, return value
// expect string with mocked value to be printed
}
When I uncomment the "automock" line I get this error:
custom attribute panicked
message: automock can only mock inline modules, not modules from another file. More information may be available when mockall is built with the "nightly" feature.
What is the proper syntax for mocking this module? thanks
Seems pretty gross and hideously awful disgusting horrendous syntax to me that Rust forces you to use some jank OOP "trait" construct just to be able to mock a single function.
It's a mismatch in POV which seems to happens frequently with people coming from managed languages. It's absolutely possible to write functions so that they are composable and testable without requiring mocking. You have just have to shift your POV on the problem. The same kind of shift required by inversion of dependency but in another dimension still. That's the best I can explain.
Generally (but not always) it entails pushing the side effectful code to the very root of your program. So that every branch can be tested as pure-ish functions.
must be nice to work for someone who will accept this kind of half tested code! haha
Like I said before, you can choose to move the "easy things" out to other files and only test them, but you will always end up with one giant untested glue code main.rs which I would like to avoid.
If you have an actual fully tested Rust project please share it.
I don't see how this other untested C++ codebase proves anything. Nor is it even relevant to original question of how to mock a function. I guess it's better than no replies at all. maybe?
Actually, it no longer requires nightly to do this, as of Mockall 0.9.0. Sorry that I left that line in the docs, but it's fixed in the current master branch.
Um, it doesn't. I don't know exactly what @tbfleming said that caused you to respond this way (he deleted his post, FTR), but Rust doesn't force anybody to use traits if they want to use mocking.
Traits do happen to be the easiest way to use mocking, because they don't require any changes to the code under test. That's why many people who roll their own mocking solutions use them. But Mockall's approach is to use compile-time substitution to minimize changes to the code under test. Usually, it can be done with a single call to #[double]. And that works when mocking traits, structs, or modules.
The reason that Mockall can't mock a single free-standing function isn't because of some limitation in the Rust language; it's actually just a matter of namespacing. If you were to do #[automock] fn myfunction(), then what would the mock function be called? mock_myfunction? And would you call mock_myfunction_context() to set up its expectations? The number of global symbols would grow quickly as you mock multiple methods. The requirement to put mockable functions into a module really just serves to limit the number of new global symbols: one per module.