rusttddmockall

How can I return self from a method mocked using mockall?


Given the following test context which makes use of the mockall library, how can I satisfactorily return a reference to cmd that will allow me to make assertions about subsequently chained method calls?

mock! {
    Cmd {}
    impl CustomCommand for Cmd {
        fn args(&mut self, args: &[String]) -> &mut Self;
        fn output(&mut self) -> std::io::Result<Output>;
        fn spawn(&mut self) -> std::io::Result<std::process::Child>;
        fn status(&mut self) -> std::io::Result<ExitStatus>;
    }
}

#[test]
fn test_x() {
    let mut cmd = MockCmd::new();
    
    cmd.expect_args().times(1).returning(|_| &mut cmd);
    // results in:
    // expected `MockCmd`, found `&mut MockCmd`
    
    cmd.expect_args().times(1).returning(|_| cmd);
    // results in:
    // move occurs because `cmd` has type `tests::MockCmd`, which does not implement the `Copy` trait 
    // NOTE: I don't think I _can_ implement Copy for my struct because it's a newtype on top of Command

    cmd.expect_output()
        .times(1)
        .returning(|| Ok(create_output(0, vec![], vec![])));

    // Call the methods in the desired order
    let args = vec![String::from(":P")];
    let _x = cmd.args(&args).output();
}



Solution

  • This is not possible, see issue #106. The closest thing you can do, as suggested in this issue, is to return a second MockCmd and set expectations on it. This does imply a specific order in which you expect the methods to be called, though. If this is not good enough for you, you can open an issue in mockall's repository.

    #[test]
    fn test_x() {
        let mut cmd = MockCmd::new();
    
        cmd.expect_args().once().returning(|_| {
            let mut cmd = MockCmd::new();
            cmd.expect_output()
                .once()
                .returning(|| Ok(create_output(0, vec![], vec![])));
            cmd
        });
    
        // Call the methods in the desired order
        let args = vec![String::from(":P")];
        let _x = cmd.args(&args).output();
    }