rogervinas / tests-everywhere
🤠 Tests, Tests Everywhere!
Rust testing this simple Hello World with mockall
Show me the code
Implementation
- Create
HelloMessage
trait andHelloWorldMessage
implementation in hello_message.rs:
pub trait HelloMessage { fn text(&self) -> String; } pub struct HelloWorldMessage {} impl HelloMessage for HelloWorldMessage { fn text(&self) -> String { return String::from("Hello World!"); } } impl HelloWorldMessage { pub fn new() -> Self { Self {} } }
Creating it as a trait will allow us to mock it for testing.
Note that we create a HelloWorldMessage::new()
method just as a convention to create new instances, although we could just use HelloWorldMessage {}
.
- Same way create
HelloConsole
trait andHelloSystemConsole
implementation in hello_console.rs:
pub trait HelloConsole { fn print(&self, text: String); } pub struct HelloSystemConsole {} impl HelloConsole for HelloSystemConsole { fn print(&self, text: String) { println!("{}", text); } } impl HelloSystemConsole { pub fn new() -> Self { Self {} } }
- Create
HelloApp
class in hello_app.rs:
pub struct HelloApp { message: Box<dyn HelloMessage>, console: Box<dyn HelloConsole> } impl HelloApp { pub fn new(message: Box<dyn HelloMessage>, console: Box<dyn HelloConsole>) -> Self { Self { message, console } } pub fn print_hello(&self) { self.console.print(self.message.text()); } }
Note that we pass constructor arguments as Box<dyn T>
to avoid doesn't have a size known at compile-time
compilation error.
- Create main function in main.rs that wraps it all together:
fn main() { let message = HelloWorldMessage::new(); let console = HelloSystemConsole::new(); let app = HelloApp::new(Box::new(message), Box::new(console)); app.print_hello(); }
Test
Following Rust By Example > Unit testing and mockall > Getting Started guides ...
- Test
HelloMessage
in hello_message.rs:
#[cfg(test)] mod tests { use super::*; #[test] fn should_return_hello_world() { let message = HelloWorldMessage::new(); assert_eq!(message.text(), String::from("Hello World!")) } }
- Setup
HelloMessage
mocking needed byHelloApp
test in hello_message.rs:
// 2.1 Enable automock #[cfg(test)] use mockall::automock; // 2.2 Enable automock for HelloMessage #[cfg_attr(test, automock)] pub trait HelloMessage { fn text(&self) -> String; }
- Setup
HelloConsole
mocking needed byHelloApp
test in hello_console.rs:
// 3.1 Enable automock #[cfg(test)] use mockall::automock; // 3.2 Enable automock for HelloConsole #[cfg_attr(test, automock)] pub trait HelloConsole { fn print(&self, text: String); }
- Test
HelloApp
in hello_app.rs:
#[cfg(test)] mod tests { use super::*; use mockall::predicate::*; // 4.1 Use MockHelloMessage, enabled by 2.2 use crate::hello_message::MockHelloMessage; // 4.2 Use MockHelloConsole, enabled by 3.2 use crate::hello_console::MockHelloConsole; #[test] fn should_print_hello_message() { let message_text = "Hello Test!"; // 4.3 Create a mock of HelloMessage let mut message = MockHelloMessage::new(); // 4.4 Return "Hello Test!" whenever text is called message.expect_text().return_const(String::from(message_text)); // 4.5 Create a mock of HelloConsole let mut console = MockHelloConsole::new(); // 4.6 Expect print to be called once with "Hello Test!" console.expect_print() .with(eq(String::from(message_text))) .times(1) .return_const(()); // 4.7 Create a HelloApp, the one we want to test, passing the mocks let app = HelloApp::new(Box::new(message), Box::new(console)); // 4.8 Execute the method we want to test app.print_hello(); // Expectation 4.6 will be checked once test ends } }
- Test output should look like:
running 2 tests test hello_app::tests::should_print_hello_message ... ok test hello_message::tests::should_return_hello_world ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Happy Testing! 💙
Top comments (0)