`pub` only for tests

#1

Is there a way to allow some functions to be exported only for use in tests?
e.g. I have a pub struct Foo { ... private fields ... }, and I want to export a function that verifies the object is in some state, but it should only be called by tests in other crates.

My first attempt was

#[cfg(test)]
pub fn verify_state(foo: &Foo) {
  if !foo.bar {
    panic!("...");
  }
}

but it seems that the “test” configuration does not propagate to dependencies. Which makes sense to reduce compile times, but it also means that verify_state would have to be available in non-test code as well.

There’s nothing inherently wrong with making it visible, just that if you need to use it in prod code, you might be using the library wrong, so I would like to enforce no uses in prod (warnings during compilation would probably be fine too).

Is that achievable?

#2

Do not know how idiomatic that is, but I once solved this problem by just adding feature _test, obviously disabled by default. External dependent package may, of course, use it, but _ in front should indicate that this is not desirable.

1 Like
#3

I think the best solution available is to provide the function always, as pub fn Foo::validate(&self) -> Result<(), InvalidState>, and a note in the documentation that says the function is intended for testing.

Of course, a better solution would just be to make invalid states impossible to construct via the public API, and then that said function could just always return “yes, it’s valid”.

#4

I’ve done combination of two things:

  • Created a test_support submodule, documented it that the content should be used only in tests.
  • Added a trait (eg TestFoo) into the module and implemented it for the Foo.

This makes the method directly available on Foo, but only after the TestFoo trait is imported from the test_support module. I trust the users of the crate not to import that trait by accident.

1 Like
#5

There are multiple things which can be done here.

  1. #[doc(hidden)] which is very common, even in the std code (rg '#\[doc\(hidden\)\]' | wc -l gives 204 occurrences)
  2. if cfg!(debug_assertions) { panic!("Not intended to run in production code") }
  3. pub(crate), which means that you this is public, but only to this crate, which means that you only can use it for unit-tests, but not for integration tests
  4. write a proc macro (and release it) that will print warnings during compilation when a function is annotated with a certain attribute (e.g. #[{debug,release}_only] or with an level, e.g. #[{debug,release}_only({warning,error}). Would be awesome!)
2 Likes
#6

Thanks for all the great suggestions! In the end, I went with a combination of pub(crate) and pub mod testing, with the expectation that consumers of the crate will not try to use testing functions outside their own tests.