如何在Rust中测试私有方法?

36

如何在Rust中测试私有方法? 我没有找到任何相关信息。文档中也没有相关信息。

3个回答

36
使用#[test]时,私有方法和公有方法并没有什么特别之处 - 你只是编写可以访问其能够访问的任何内容的完全正常的函数。
fn private_function() {
}

#[test]
fn test_private_function() {
    private_function()
}

如果您正在使用Cargo,则外部测试(例如tests/*.rsexamples/*.rs)或文档测试不会访问私有成员;它们也不应该:这些测试旨在成为公共 API 测试,而不是处理实现细节。


21
外部测试应绝对被允许访问私有部分,因为1)您可能需要访问私有部分才能可靠地触发公共API中的所有代码路径。例如,可能存在一个内部阈值,当达到该阈值时会触发不同的代码路径。您还可能需要故障注入或内部细节来触发边缘情况。2)访问私有部分可以为测试失败提供更好的诊断 - 您可以将内部信息插入恐慌消息等... - kralyk
6
诊断:这就是 std::fmt::Debug 的作用。至于其他方面,我坚持我的立场,即外部测试不应该访问私有细节。外部测试并不适合您所描述的情况——这就是内部单元测试的作用。 - Chris Morgan
4
在某些情况下,fmt::Debug 过于粗略 - 例如,当您只想获取特定位时,可能不希望打印有关整个大型数据结构的细节。无论如何,是否适合在内部测试中设置公共API的实际用例?如果不行,那行不通,因为我需要这样做。如果可以,那么好吧,我很高兴将其称为内部测试,尽管此时区分有点无意义?核心问题在于,无论您如何努力,实现细节总会以某种方式影响接口,这是无法避免的。 - kralyk
黑白二元论在多目标优化中失败。理想情况下,外部测试不应访问应用程序的私有内部。但是,有竞争的理想需要平衡。有时,获得良好的测试覆盖率最实际的方法是直接深入挖掘和调整某些私有内容。当然,您可以添加额外的代码复杂性以使其不必要,但这是有成本的。有灵活性根据具体情况做出这些决策是有用的。 - Drew Nutter
这就是为什么测试需要访问私有内部的原因。如果上述论点并不十分令人信服:想象一下,我正在编写一个作为其他“东西”容器的“对象”。我可能希望能够将数据“推入”该对象中,并以某种方式“取回数据”,但我可能不想暴露“其中有多少东西”或任何与内部实现细节相关的内容。很多情况都是如此。你可以包装一个std::container,但不暴露.len()。然而,对于测试,我希望查询.len(),因为我可能会放入一些数据,并希望检查它是否存在。 - FreelanceConsultant

8

我不知道这个问题对你是否仍然存在,但我找到了一些相关文档:

测试组织

我从中得出的结论是,您可以测试私有方法,但只有在测试可以看到它(即它们在同一作用域中)时才能进行测试,因为测试遵循与任何其他函数相同的可见性规则。

这是一个可行的示例:

pub fn add_two(a: i32) -> i32 {
    internal_adder(a, 2)
}

fn internal_adder(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn internal() {
        assert_eq!(4, internal_adder(2, 2));
    }
}

总之,请记住测试社区内关于私有方法是否应该被测试的辩论仍然存在。双方都有合理的观点,正确答案只取决于您、您对测试程序的看法和项目的上下文。

1
我有一个问题,这段代码会导致警告:warning: function is never used: internal_adder``,但是internal_adder函数在internal函数中被调用。这是因为测试代码是死代码吗?我没有找到任何描述这个问题的文档。我知道添加pub将消除此警告,这与其他编程语言中的export有些相似。 - liby
1
@liby,“internal”函数不是最终可执行文件的一部分,而是测试的一部分,这是您在编写“#[cfg(test)]”时指定的内容。当您使用“cargo run”或“cargo build”时,测试既不会运行也不会包含在可执行文件中。因此,编译器会提示“internal_adder”未被使用。 - dotmashrc

0
一个没有在上面提到的解决方法是,在实现中在公共方法中编写私有方法的测试,然后在测试中调用该方法....虽然不理想,但也可以。
一些伪代码:
impl MyStruct {
   fn new() -> Self { ... }
   fn my_priv_method1(&self, x) -> u8 { ... }

   pub fn self_test_private_methods() {
     assert(self.my_priv_method(1) == 2);
   }
 }
#[cfg(test)]
mod tests {
   use super::*;

   #[test]
    fn internal() {
        MyStruct::new().self_test_private_methods()
    }
}

这是不必要的。Rust允许您直接测试私有方法: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=743c2b7e1b9c9beb244604bad5760a58 - undefined
谢谢您的评论,我已经修正了答案,请您有时间时再次查看。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接