Separating End to End Tests in Rust with Cargo Features

Justin Wernick <>
Curly Tail, Curly Braces
2026-07-17

Abstract

I've recently added a new way to run heavy end to end tests to one of my Rust projects. These tests are launching heavy infrastructure like Docker and Selenium WebDriver, so I did not want them to run together with my unit tests. In this article, I explain how I added a test target that is skipped when I run cargo test, and is run on its own when I run cargo test-end-to-end.

I've recently solved a problem I was having in my Rust tests, and I wanted to share my solution.

I have a web app that I've been working on, written in Rust of course. The build toolchain isn't anything fancy, I'm mostly sticking to what Cargo has to offer.

The problem: heavy end to end tests

The problem started when I wanted to start adding end to end tests. In the context of web applications like this one, end to end testing means starting up the web app, including any infrastructure it depends on, and interacting with it using a real web browser via Selenium WebDriver. Infrastructure-wise, I currently use a SQLite database for simplicity, but will probably need to more to a server like PostgreSQL as the project matures. The tests also require a Git server running, since my web app can sync user data using Git.

In short, these tests are much heavier than my unit tests. They require starting up some Docker containers and running real web browsers.

I don't want to run them automatically when I type cargo test. In local development, I'd like to run just the unit tests most of the time, and then run the end-to-end tests only before committing.

Desired outcome

I would like to do the following:

The Solution

This is actually possible using only Cargo and feature flags.

The new tests live in the tests subdirectory of my web app crate. This is the standard place for integration tests, which will run after the web app's binary is built.

The trick is to add a new feature in the web server's Cargo.toml. The feature should be disabled by default.

# Cargo.toml

[features]
end-to-end-tests = []

Once you have this feature, you can add a custom test target that requires that feature.

# Cargo.toml

[[test]]
name = "end-to-end"
path = "tests/end-to-end.rs"
required-features = ["end-to-end-tests"]

This is already most of the way there! These integration tests will now be skipped when you run cargo test, unless you enable the feature with cargo test -F end-to-end-tests.

The next thing to solve is how to run only these tests. You can target this test suite with cargo test --test end-to-end -F end-to-end-tests. The only downside is that it's a pain to type. Luckily, Cargo has alias support. You can add an alias in .cargo/config.toml.

# .cargo/config.toml

[alias]
test-end-to-end = "test --test end-to-end -F end-to-end-tests"

Just like that, when we run cargo test-end-to-end it will use our alias to only run our end to end tests, including the feature to enable them.

Taking more control over setup and tear-down

Cargo's normal test harness, libtest, is great for unit tests. However, when it comes to end to end tests, the lack of a 'before all' and 'after all' step for setup and tear-down becomes a problem. The tests all running in parallel also isn't ideal for these end to end tests.

Luckily you're not stuck with libtest. If you add harness = false to your test target config then Cargo will run your main function.

# Cargo.toml

[[test]]
name = "end-to-end"
path = "tests/end-to-end.rs"
required-features = ["end-to-end-tests"]
harness = false

I'm going to leave what to do with that main function as an exercise for the reader. I personally used a crate called libtest-mimic, so that it could do the parts of libtest that I still wanted, like bring able to filter to a specific test case on the command line, while still being able to start my Docker containers and web server before any tests ran, and shut them down at the end.

Conclusion

So that's my trick for having end to end tests that aren't included in my unit test runs.

To recap, the method is to:

I hope that this helps you in your own Rust testing journey!


If you'd like to share this article on social media, please use this link: https://www.worthe-it.co.za/blog/2026-07-17-separating-end-to-end-tests-in-rust-with-cargo-features.html

Copy link to clipboard

Tags: blog, rust


Support

If you get value from these blog articles, consider supporting me on Patreon. Support via Patreon helps to cover hosting, buying computer stuff, and will allow me to spend more time writing articles and open source software.


Related Articles

The Localhost Podcast CLI Tool

I use a podcast player to queue up media from my computer to watch on my phone. I've recently written a new CLI tool to streamline my process.

The Newtype Pattern in Rust

Programming design patterns are patterns that come up in a variety of different situations while programming. In this article I discuss the Newtype design pattern. Specifically, I discuss it in the context of the Rust programming language, and how to solve some of the problems that arise when using the Newtype pattern in Rust.

Subscribe to my RSS feed.