dearn44
dearn44

Reputation: 3432

Common function that is used both in `tests` and `src` not found

I have a number of tests defined inside some of my modules with:

#[cfg(test)]
mod tests {
    ...

and a number of tests found inside my /tests directory. All of these happen to need a common function called get_local() that I have put inside a utils module

+src
 |-lib.rs
 |-utils.rs
 |-...
+tests
 |_common.rs

Now since this function is only used in case I run tests I thought I should add a #[cfg(test)] above it.

#[cfg(test)]
pub fn get_local() -> SomeResult { ... }

and then I make sure it is inside lib.rs:

pub mod utils;

When I try using the function in tests/common.rs though:

use my_crate::utils::get_local;

and run the tests with the test command I get the error: no get_local in utils. This error only goes away if I remove #[cfg(test)] from the declaration of get_local. Why does this happen?

Upvotes: 3

Views: 802

Answers (2)

zertyz
zertyz

Reputation: 705

As for the "why does this happen?", Kevin Reid already answered: code in tests/ are "Integration Tests" and, therefore, uses the regular version of your lib (not the test-compiled one). See Integration Testing.

You may use the approach bellow to have a common test module for a single crate. Note that, for Integration Tests, each file inside tests/ is compiled as an individual crate. You may still create modules there.

With that said, my approach to having common test code is to put them inside a "test_commons" module and use:

lib.rs:

#[cfg(test)]
mod test_commons;

The contents of test_commons.rs has nothing special -- functions are defined without any #[cfg(test)] annotation.

Then, to use it:

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_commons::{self,ContainerKind,Blocking};
...

For Integration Tests inside tests/ to share that same code, simply use:

#[path = "../../src/test_commons.rs"] mod test_commons;

Upvotes: 1

Kevin Reid
Kevin Reid

Reputation: 43782

Why does this happen?

#[cfg(test)] is only active when the crate is being compiled into a test binary (instead of a library or normal binary) — the same condition under which #[test] functions are compiled to be run rather than ignored. But for a tests/ test, the crate being compiled in test mode is not your library — it's a separate binary crate for the test, which Cargo sets up to depend on your library compiled normally.

Thus, tests/ tests may only test the "public API" of your library — they see it no differently than a dependent crate outside your library.

As a workaround, you can use #[doc(hidden)] instead of #cfg(test)] so that the function is always available but undocumented, or you can put it in a module which is compiled into the test as well as the main library (but it's tricky to write code that works in both cases).

Upvotes: 2

Related Questions