Closed gregdavisfromnj closed 5 months ago
Sure there is. All you need to do is to export Client and MockClient from the same module. Perhaps like this:
mod mongodb {
pub use ::mongodb::Client;
#[cfg(test)]
mock! {
pub Client
...
}
}
#[double]
use mongodb::Client
Ah, okay... this was a bit of Rust I was not aware of being able to do with modules and redeclaring things that are really defined in another crate. My case is trickier though, and so to use another random mongodb type that is not mocked, for example ClientOptions
below, I had to make the declaration like this to be able to resolve it:
use mongodb::options::ClientOptions; // import to satisfy usage in main function
mod mongodb {
pub use ::mongodb::Client;
pub mod options {
pub use ::mongodb::options::ClientOptions;
}
#[cfg(test)] // import to satisfy usage in mock macro
use mongodb::options::ClientOptions;
#[cfg(test)]
mock! {
pub Client {
...
pub fn with_options(options: ClientOptions) -> mongodb::error::Result<Self>;
}
}
}
#[double]
use mongodb::Client
fn main() {
...
/* create a ClientOptions */
/* create Client from with_options, passing it the ClientOptions */
/* pass the Client to a cool_service::CoolService */
...
}
Now suppose the CoolService is in another file named cool_service.rs. The use
statement to import mongodb::Client in that file, using the double
macro, seems to require the crate::
prefix to make sure the MockClient is found in a different file for test build configuration. Like this:
// cool_service.rs
#[double]
use crate::mongodb::Client;
Going back to the hypothetical first file where main
is defined, I can seem to use either use crate::mongodb::Client;
or use mongodb::Client;
for the import because they seem to mean the same thing there.
And finally, in case anyone else ever goes down this path like me... I was able to move the contents of the mongodb module defined above to its own module as a file name mongodb.rs, and then replace the module definition with a declaration of the module like mod mongodb;
. Because MockClient is actually used in that file during the test configuration, and not mongodb::Client, I needed to annotate the pub use ::mongodb::Client;
line with #[cfg(not(test))]
to avoid an unused-import warning during normal build.
Thanks for the quick response!
Hello, I have some test code that directly mocks structs in the MongoDB crate. In my case, I have references to the mocked struct at two layers, architecturally speaking, and so the Rust compiler needs to deal with mock type usage declarations in two modules. I have the runtime and test usage of mocks and non-mocks working with Mockall, but I ultimately have resorted to the following
use
statements, which I see is not one of the test cases in the mockall_double crate:...where
MockClient
is defined by amock!
macro in a module that is internal to my codebase (mocks::mongodb
).I don't see a way to get the
mockall_double::double
macro to generate my example block above, without creating an optional parameter for the attribute macro that takes the type name to use for the test configuration instead of assuming that the type name should be macro input type name prefix with "Mock". Is there a way of organizing my mocks into submodules that does not require redefinition of all other structs, etc, in the external crate that are not mocked and so that they are still accessible in the outer layer (and thus allowing me to usemockall_double::double
as-is)?Or, would you be open to a PR that adds a parameter to the
double
macro so it can generate the block above from something like this:Getting the declaration of the "double" down to one
use
statement instead of two conditional statements would make for less lines of code, of course, but also prevents situations where my IDE reorganizes the use statements and splits them up in a way that makes it non-obvious that they are closely/semantically related.