DEV Community

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

Posted on • Edited on

2

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! 💙

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️