Closed guvenir18 closed 8 months ago
I have solved this issue, but now I have another problem. Lets say I am writing test for a struct UnderTest and this struct requires Service_A which we have mocked. Problem is when I run cargo test, it compiles my whole project and somewhere in other modules, a struct AnotherStruct expects Service_A, not MockService_A but I want MockService_A used only for current module where source code and unit tests of UnderTest are.
Even though you say you've solved your first problem, I'll give the solution here for the benefit of future readers:
Mock objects have no members. You can't even declare them, which was the source of your error message. Instead, mock objects only have methods and traits.
As for the second problem, you just need to be more careful with your use
statements. Try putting the appropriate use
statements (without or without mockall_double::double
, as appropriate) in each module.
Yes, I did your solution.
About second one, I did not understand how I should handle use statements. I thought cargo would compile each test case separately so if I do
#[cfg(not(test))]
use crate::Services::Service_A;
#[cfg(test)]
use mocks::MockService_A as Service_A;
pub struct UnderTest {
service: Service A,
}
on a source file, I thought it will only change Service_A with MockService_A on current file and other parts of the code would be unaffected but cargo compile whole project when I run cargo test and since UnderTest uses MockService_A, code pieces that use Service_A throught UnderTest gives "expected Service_A, got MockService_A" compilation error. An example would be on anıother file which uses UnderTest through use crate:
let new_service = UnderTest::new(service);
-> gives compilation error since this part normally expects Service_A but gets mock one. What I want is MockService_A will only be used for this test module and nowhere else.
I dont know how to handle this situation. What would be best practice here. I would really appreciate your help
When you build unit tests, cargo compiles your whole lib or bin into a single executable. And Mockall doesn't do anything magical with type replacement. It just relies on those #[cfg(...)] use
statements to replace original types with mock types at compile time. Those use
statements each have module scope, so you simply need to ensure that you use the correct ones in each module.
When using mocks, the best practice is to cleanly layer your code. For each layer but the lowest, write unit tests that use a mock version of the lower layer. It should look something like this:
mod highest {
#[double]
use crate::lowest::Bar;
struct Foo {
bar: Bar // in unit tests, this will really be MockBar
}
#[automock]
impl Foo {...}
#[cfg(test)]
mod t {
use super::*;
#[test]
fn my_test() {
let mut mock = Bar::default(); // this is really MockBar
let foo = Foo::new();
...
}
}
}
mod middle {
#[double]
use crate::lowest::Baz;
struct Bar{
baz: Baz // in unit tests, this will really be MockBaz
}
#[automock]
impl Bar {...}
#[cfg(test)]
mod t {
use super::*;
#[test]
fn my_test() {
let mut mock = Baz::default(); // this is really MockBaz
let bar = Bar::new();
...
}
}
}
mod lowest {
struct Baz {}
#[automock]
impl Baz {...}
}
Your example clarified everything, I made it work. One last question. I configured my code so it uses mocks in test but thing is I also have integration tests which are labeled with #[cfg(test)]
so when compiler tries to build integration tests, it fails. How should I handle this situation ? I was thinking setting a feature in Cargo.toml but it may really compilicate cfg statements.
I have solved this issue to. Previously, I put my integration tests under src directory but this was wrong since they have to be on same level with src folder so cargo can treat them as an external crate. Thanks for your help, you can close this issue.
Glad to help.
Hello. First of all, thank you for your work on this great crate. I am still learning Rust and I would really appreciate any help I can get.
I have to mock an internal struct, lets say Service_A which is like:
Where Ext_Service is another struct from external crate I have no control over. I am trying to mock this like that:
Then use it like that:
My question Is, do I have to mock Ext_Service to mock Service_A ?. I was thinking about maybe I can pass some kind of NULL value instead of Ext_Service but it is not possible in Rust without wrapping field in Option<> as far as I know. Also, I am not sure about if my mocking is correct because I get an error from macro which says "message: impl block must implement a trait" . Thanks for help