DEV Community

Cover image for Tests Everywhere - Rust
Roger Viñas Alcon
Roger Viñas Alcon

Posted on • Edited on

Tests Everywhere - Rust

GitHub logo rogervinas / tests-everywhere

🤠 Tests, Tests Everywhere!

Rust testing this simple Hello World with mockall

Show me the code

Implementation

  1. Create HelloMessage trait and HelloWorldMessage 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 {}
  }
}
Enter fullscreen mode Exit fullscreen mode

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 {}.

  1. Same way create HelloConsole trait and HelloSystemConsole 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 {}
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. 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());
  }
}
Enter fullscreen mode Exit fullscreen mode

Note that we pass constructor arguments as Box<dyn T> to avoid doesn't have a size known at compile-time compilation error.

  1. 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();
}
Enter fullscreen mode Exit fullscreen mode

Test

Following Rust By Example > Unit testing and mockall > Getting Started guides ...

  1. 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!"))
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Setup HelloMessage mocking needed by HelloApp 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;
}
Enter fullscreen mode Exit fullscreen mode
  1. Setup HelloConsole mocking needed by HelloApp 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);
}
Enter fullscreen mode Exit fullscreen mode
  1. 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
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode

Happy Testing! 💙

Top comments (0)