Unused and testing

I have a structure with an implementation function I use for testing from another module. That is, the test suite for rolodex uses the person::build(...) method to build some people to test the rolodex interaction with people. The regular code for the program uses a different (person::to_person()) method to build person instances. However, using that from teh rolodex test would be quite messy.
Is there a convention for this situation?
Thanks,
Joel

if the person::build() function is only used in tests, or, in other words, then it should reside in the tests module, just like any testing helper functions.

by convention, unit tests are usually submodules of the code being tested, thus they have access to private items of the implementation.

on the other hand, if the tests are not in the same module or sub-module of the implementation, e.g. integration tests, then they should NOT be able to access private items, and only test via the public interface.

something like this:

// the implementatin
pub mod person {
    #[derive(Debug)]
    pub struct Person {
        id: u32,
    }

    // public interface
    impl Person {
        pub fn to_person(id: u32) -> Person {
            Person { id }
        }
        pub fn id(&self) -> u32 {
            self.id
        }
    }

    // unit tests in a submodule
    // could be in a separate `tests.rs` file
    #[cfg(test)]
    mod tests {
        use super::*;

        // helper function/method for unittests
        impl Person {
            fn build(id: u32) -> Self {
                Person { id }
            }
        }

        #[test]
        fn my_unittest() {
            let person = Person::build(42);
            // can access private field
            assert_eq!(person.id, 42);
        }
    }
}

// example interation tests, use a `mod` here for demonstration purpose only
// typically in separate compilation units, like `$CARGO_MANIFEST_DIR/tests/*.rs`
#[cfg(test)]
mod tests {
    use crate::person::*;

    #[test]
    fn my_integrationtest() {
        //let person = Person::build(42); //<- error: `build()` is private
        let person = Person::to_person(42);

        //assert_eq!(person.id, 42); //<- error: `id` is private
        assert_eq!(person.id(), 42);
    }
}

That is very much the tension I am dealing with. The test in question is not a test of person. The rolodex structure holds people. It needs to be able to add them in (assigning an id), fetch them by id, delete them, and reclassify them (archived vs active). To test this, I need to fee the rolodex some people. Still, this feels like a unit test of rolodex, rather than an integration test. And even as an integration test, using the interface the gui uses would be somewhat interesting. I was hoping there was a better way to do this. (I can pretend it is a person unit test, since it uses only the public methods from the rolodex. But that seems a strange setup.)
Yours,
Joel

The other option is to add #[cfg(test)] to the function that is used only in the tests. This way, it will not provoke any dead code warning, and it won’t be accidentally usable outside of tests.

if I understand it correctly, the rolodex depends on private implementation details of person? so they must be defined in the same module (or in a submodule)?

sometimes, it's reasonable to expose some internal information for testing purpose only, e.g. a private field or a helper function, but these should be behind a conditional compilation guard, e.g. #[cfg(test)], as already suggested.

in rust, the default rule is that a submodule can use private items in the ancester module (super module, super-super module, and so on), but not the other way around.

you can change the default visibility with an explicit pub (/*scope*/) specifier, but this only allows private items to be exposed to an ancestor module, you cannot put an arbitrary module that does not resolve to an ancenstor of the current module:

mod blahbalh {
  mod person {
    pub struct Person {
      pub name: String,
      // this field is not part of a public API,
      // but we make it accessible in `blahblah`,
      // and importantly, also in its submodules
      pub(in super) id: u32,
    }
  }

  mod rolodex {
    // you can access the `id` field of `Person` within this module too
    use super::person::Person;
    pub struct Rolodex {
      people: Vec<Person>,
    }
    impl Rolodex {
      pub fn add(&mut self, name: impl Into<String>) {
        let next_id = todo!();
        self.people.push(Person {
          name: name.into(),
          id: next_id,
        });
      }
    }
  }
}

I understand why it sounds like the rolodex depends upon private properties of person. It doesn't. It just needs some person(s) to put in its tables. The problem I have is that the construction method used by the iced gui is carefully matched to the gui state data structure. So using that for the rolodex test is somewhat awkward. Do I understand that I can mark the build method as #[cfg(test)], even though the code is not in a test module? That would seem to get rid of the unused code warning and still let me use the method. The other alternative is to jump through the hoops needed to use the construction method the gui uses. As this is a rolodex test, not a person test, I am not trying to check how the builders work.
Yours,
Joel

yes.

the #[cfg(test)] annotation means the method only exists when you are building and running the tests, e.g. with cargo test; otherwise it simply disappears.

Thanks. That is what I was missing.