Closed naftulikay closed 6 months ago
run
is definitely getting called. That's why pre_execute
is being called, after all. And pre_execute panics because you never set any expectations for it. Unless you're using the nightly
flag, you need to add some variation of .returning
to every expectation . I'm actually surprised that this compiles. There shouldn't be any method bodies inside of mock!
, because they don't do anything there.
@asomers thanks for your response! I have updated my test code and I'm still seeing the failure.
tests.rs:
use anyhow::Result;
use mockall::mock;
use super::Command;
mock! {
PreExecFailure{}
impl Command for PreExecFailure {
// mock these three methods only, not run
async fn pre_execute(&self) -> Result<()>;
async fn execute(&self) -> Result<()>;
async fn post_execute(&self, result: Result<()>) -> Result<()>;
}
}
#[tokio::test]
async fn test_pre_exec_failure() {
let mut mock = MockPreExecFailure::new();
mock.expect_pre_execute().returning(|| Err(anyhow::anyhow!("Error")));
mock.expect_execute().returning(|| Ok(()));
mock.expect_post_execute().returning(|_r| Ok(()));
assert!(mock.run().await.is_err(), "Command::run should have returned an error on failed pre_execute");
// execute should never have been called
mock.expect_execute().never();
// pre-execute should have been called once
mock.expect_pre_execute().times(1);
}
The other source file has not changed.
Here is the failed output:
MockPreExecFailure::pre_execute: Expectation(<anything>) called 0 time(s) which is fewer than expected 1
I can observe that execute
was never called, but pre_execute
was never called according to the mock. With debug points, I can see that pre_execute
was indeed called, but I don't know why it doesn't register as having been called.
You can't set expectations after calling the code under test. Mockall doesn't work that way. You have to set them before.
I was able to resolve the issue, full code:
tests.rs:
use anyhow::{anyhow, Result};
use mockall::mock;
use super::Command;
mock! {
Exec{}
impl Command for Exec {
// mock these three methods only, not run
async fn pre_execute(&self) -> Result<()>;
async fn execute(&self) -> Result<()>;
async fn post_execute(&self, result: Result<()>) -> Result<()>;
}
}
/// A pre-exec failure should mean that exec is never called, nor post-exec
#[tokio::test]
async fn test_pre_exec_failure() {
let mut mock = MockExec::new();
// pre execute should be called and fail
mock.expect_pre_execute()
.once()
.returning(|| Err(anyhow::anyhow!("Error")));
// execute should never be called
mock.expect_execute().never().returning(|| Ok(()));
// post execute should never be called
mock.expect_post_execute().never().returning(|_r| Ok(()));
// run it
assert!(
mock.run().await.is_err(),
"Command::run should have returned an error on failed pre_execute"
);
}
/// An exec failure combined with post-exec propagation, should fail
#[tokio::test]
async fn test_exec_failure() {
let mut mock = MockExec::new();
// pre execute should be called and succeed
mock.expect_pre_execute().once().returning(|| Ok(()));
// execute should be called and fail
mock.expect_execute()
.once()
.returning(|| Err(anyhow!("Fail")));
// post execute should be called with an error
mock.expect_post_execute()
.withf(|r| r.is_err() && r.as_ref().is_err_and(|e| e.to_string().eq("Fail")))
.returning(|r| r);
// run it
assert!(
mock.run().await.is_err(),
"Command::run should have returned an error on failed execute/post-execute"
);
}
/// An exec failure should be able to be recovered by post exec
#[tokio::test]
async fn test_post_exec_recover() {
let mut mock = MockExec::new();
// pre execute should be called and succeed
mock.expect_pre_execute().once().returning(|| Ok(()));
// execute should be called and fail
mock.expect_execute()
.once()
.returning(|| Err(anyhow!("Fail")));
// post execute should be called, receive an error, and recover
mock.expect_post_execute()
.withf(|r| r.is_err() && r.as_ref().is_err_and(|e| e.to_string().eq("Fail")))
.returning(|_r| Ok(()));
// run it
assert!(
mock.run().await.is_ok(),
"Command::run should have recovered from error in post exec"
);
}
I wasn't aware that expectations were evaluated on drop()
, I had assumed that you configured the mock first, ran the code, and then after running, validate the assumptions.
I've been through the docs and I'm having trouble figuring out how to test the functionality of a default trait implementation.
I have the following trait:
command.rs
Essentially, calling the
run
method should invoke thepre_execute
hook, and if this does not succeed, theexecute
hook should not be executed. Ifpre_execute
does succeed, it should call theexecute
hook and then callpost_execute
with the output ofexecute
.I'm now writing tests to ensure that the default implementation of
Command::run
does what it should. Here is my test:command/tests.rs:
When I drop debug breakpoints into my code, it appears that
run
is never actually called, and my test case fails:Is there a different method I should be using to test the default functionality of the
Command::run
implementation?