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)