Workspace, Trait, Test and Run

Setting up a Cargo workspace is the cleanest way to manage both your aggregator library and the aggregator_app binary in one place.


Ⅰ. Setting Up a Cargo workspace

1. Create a new workspace folder

Make a parent folder (say aggregator_workspace):

mkdir aggregator_workspace
cd aggregator_workspace

2. Create the workspace manifest

Inside it, create a Cargo.toml file:

[workspace]
members = [
    "aggregator",
    "aggregator_app",
]

This tells Cargo that both crates are part of one workspace.


3. Add your crates

Now create the two crates inside the workspace:

cargo new aggregator --lib
cargo new aggregator_app

Now the folder structure looks like this:

aggregator_workspace
├── Cargo.toml      # Workspace manifest
├── aggregator      # Library crate
│   ├── Cargo.toml
│   └── src/lib.rs
└── aggregator_app  # Binary crate
    ├── Cargo.toml
    └── src/main.rs

4. Define the library

Edit aggregator/src/lib.rs:

pub fn summarize(text: &str) -> String {
    format!("Summary: {}", text)
}

5. Link the binary to the library

In aggregator_app/Cargo.toml, add the dependency:

[dependencies]
aggregator = { path = "../aggregator" }

6. Use it in the binary

Edit aggregator_app/src/main.rs:

use aggregator::summarize;

fn main() {
    let text = "Rust makes systems programming fun and safe!";
    let summary = summarize(text);
    println!("{}", summary);
}

7. Build and run

From the workspace root:

cargo run -p aggregator_app

Output:

Summary: Rust makes systems programming fun and safe!

✅ Now you have:

  • aggregator → library crate
  • aggregator_app → binary crate
  • managed together in one workspace.

Ⅱ. How to Test the Library

Let’s extend the workspace so you can test your aggregator library while also running the app. There are two ways to test library, one is adding tests inside the library and the other is adding separate integration tests in a tests/ directory


1. Add tests inside the library

Edit aggregator/src/lib.rs:

/// Summarize a given text by prefixing it.
pub fn summarize(text: &str) -> String {
    format!("Summary: {}", text)
}

// Unit tests go in a special `tests` module.
#[cfg(test)]
mod tests {
    // Bring outer functions into scope
    use super::*;

    #[test]
    fn test_summarize_simple() {
        let text = "Hello Rust";
        let result = summarize(text);
        assert_eq!(result, "Summary: Hello Rust");
    }

    #[test]
    fn test_summarize_empty() {
        let text = "";
        let result = summarize(text);
        assert_eq!(result, "Summary: ");
    }
}

2. Run the tests

From the workspace root:

cargo test -p aggregator

Output (example):

running 2 tests
test tests::test_summarize_simple ... ok
test tests::test_summarize_empty ... ok

test result: ok. 2 passed; 0 failed

3. Run the app

From the same root:

cargo run -p aggregator_app

Output:

Summary: Rust makes systems programming fun and safe!

4. (Optional) Integration tests

You can also add separate integration tests in a tests/ directory inside the aggregator crate:

aggregator/
├── Cargo.toml
├── src/lib.rs
└── tests
    └── integration_test.rs

tests/integration_test.rs:

use aggregator::summarize;

#[test]
fn integration_summary() {
    let text = "Integration testing is easy!";
    let result = summarize(text);
    assert_eq!(result, "Summary: Integration testing is easy!");
}

Run all tests in the workspace:

cargo test

✅ Now you have:

  • Unit tests inside src/lib.rs
  • Integration tests in tests/
  • Ability to run tests and app from the same workspace.