Problems sharing test code (traits) via a testutils crate

Here's what's happening under the hood when you run tests for the core crate:

  1. Cargo compiles the core crate as a regular library.
  2. Cargo compiles the testutils crate as a library, using the core library from step 1 as a dependency.
  3. Cargo compiles the core crate again with rustc --test (which produces an executable binary) using the testutils library from step 2 as a dependency.
  4. Cargo runs the core test binary from step 3.

Notice that the core crate is compiled twice (in steps 1 and 3), and the unit test binary uses both copies of the generated code. This is why Cargo is complaining that you have two different traits endbasic_core::exec::BuiltinCommand and exec::BuiltinCommand.

Some possible solutions, depending on your needs:

  • Change these unit tests into integration tests. Then they will link to the core library from step 1, just like the testutils crate does, so both of them will use the same copy of the BuiltinCommand trait. (However, integration tests can't directly test private code from the core library.)

  • Instead of a separate crate, include the testutils code as a module within both the core crate and the cli crate. Then the testutils code will be compiled twice, but it will not introduce any circular dependencies.

  • Rewrite the testutils crate so it does not depend on core. Move code like impl core::BuiltinCommand { ... } into one or both of the core or cli crates. However, this might require duplicating a lot of code.

1 Like