Sling Academy
Home/Rust/Testing in Rust: Using Integration Tests in a Separate Tests Folder

Testing in Rust: Using Integration Tests in a Separate Tests Folder

Last updated: January 04, 2025

Testing is an essential part of software development, especially when working with a systems programming language like Rust. While unit tests are great for testing individual components, integration tests help verify that multiple parts of your code work together as intended. In Rust, integration tests are generally placed in a separate directory from your source code and your unit tests, which helps organize larger projects more cleanly.

Understanding Rust's Testing Structure

In Rust, tests are categorized mainly into two types: unit tests which reside in the same file as the code they are testing, and integration tests which generally reside in a dedicated tests directory at the top level of your project.

Setting Up Your Integration Tests

To set up integration tests, start by creating a directory named tests in the project's root. This directory is where you'll place your test files:

.
├── Cargo.toml
└── src
    ├── main.rs
    └── lib.rs
└── tests

Each Rust file in the tests directory is compiled as a separate crate. As an example, let's say you have a file called tests/integration_test.rs. In this file, you can define functions annotated with #[test] to mark them as test functions:

// tests/integration_test.rs

#[test]
fn it_adds_two() {
    let result = my_project::add(2, 2);
    assert_eq!(result, 4);
}

Writing a Simple Integration Test

Assume you have the following function in a crate's lib.rs file:

// src/lib.rs

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

You can test this functionality using an integration test as follows:

// tests/integration_test.rs

extern crate my_project;

#[test]
fn test_adding_two_numbers() {
    assert_eq!(4, my_project::add(2, 2));
}

Running Your Integration Tests

Running integration tests in Rust is straightforward. Use the command cargo test from your project root. This command picks up both unit tests and any integration tests you've defined:

$ cargo test

This command runs every function annotated with #[test] in your source directories and the tests directory and reports pass/fail statuses accordingly.

Using common Submodule for Definitions

In many cases, you'll want to share setup code between multiple tests. Create a common module inside the tests directory by adding a subdirectory called common with an empty mod.rs file:

tests
├── integration_test.rs
└── common
    └── mod.rs

Then you can define helper methods or setup functions inside this module:

// tests/common/mod.rs

pub fn setup() {
    // place setup test code here
}

You can use the setup code in your test files by calling the setup function:

// tests/integration_test.rs

mod common;

#[test]
fn test_with_setup() {
    common::setup();
    assert_eq!(3 + 2, 5);
}

Conclusion

Integration testing in Rust is a powerful way to ensure that all your components interface correctly. By organizing tests into separate folders, you gain a clear structure that promotes better maintainability as your project grows. With Rust's robust cargo test suite, running complex integration workflows becomes effortless, providing you confidence in your software quality.

Next Article: Running Examples and Benchmarks with Cargo for Rust Projects

Previous Article: Exporting Types and Functions in Rust via Re-exporting Modules

Series: Packages, Crates, and Modules in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior