Reputation: 905
I would like to write a unit test that asserts that a method is private. The test has visibility to the method and can execute and test it.
The additional test I want is in order to confirm that the method remains private, i.e. I want to be able to fail a test if a future coder (or myself) "accidentally" changes the visibility to pub. Making the method public would break the design because it would allow clients of the implementation to change the struct in a way that would violate assumptions made in the rest of the design.
Is there some type of introspection or reflection that will allow this?
Upvotes: 1
Views: 381
Reputation: 10136
As pointed out in the comments, field/function visibility is a concept that only exists at compile time, so you will need to find a way to test that some code fails to compile.
Doctests are a obvious tool, but are somewhat of a sledgehammer solution. I'd recommend the trybuild
crate instead. I've mostly seen it used by macro authors, but it can generally be useful whenever you want to test that something either does or doesn't compile.
An example usage might be:
// src/lib.rs (could be anywhere really, it just needs to be a regular #[test] function)
#[cfg(test)]
#[test]
fn ui_tests() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/fail/*.rs");
}
// tests/fail/private_field_access.rs
fn main() {
let my_struct = MyStruct::new();
println!("{}", my_struct.private_field);
}
This will generate a corresponding .stderr
file that you can check into version control (similar to rustc's "ui" tests). When the tests are run, the file is compiled, and the output from the compiler is compared against the actual error.
This means you can catch regressions in a more fine-grained way than a simple binary "does it compile" check. For example, if you accidentally made the private field public, the above test may still fail to compile for some other reason (perhaps it doesn't implement Display
). trybuild
catches those regressions in a way that compile-fail
doctests cannot.
When it comes time to generate the .stderr
files, you can run TRYBUILD=overwrite cargo test
to overwrite the existing files.
It's worth mentioning that trybuild
tests (like doctests) only work for the external API of your crate, not module. If that's an issue, you could consider using cargo workspaces and a multi-crate setup (there are other reasons this might be preferable, e.g. compile times).
Upvotes: 3