我可以为集成测试和/或基准测试将一个对象设为公共对象吗?

9
根据The Book的建议,我把我的板条箱集成测试移动到了一个tests目录中。然而,其中一些测试使用了我不想在板条箱外部导出的函数,所以我不能再在集成测试文件夹中使用它们了。我也会在非测试目的中使用它们,所以它们需要在测试之外编译。我尝试使用pub(restricted)的变体,但我无法使其工作。理想情况下,我想要像pub(tests)这样的东西。

目录树(相关部分):

my_crate
|- src
   |- parser.rs
|- tests
   |- parsing.rs
|- benches
   |- parsing.rs

tests/parsing.rs:

extern crate my_crate;

use my_crate::parser::foo;

#[test]
fn temp() {
    foo();
}

benches/parsing.rs:

#![feature(test)]
extern crate test;
extern crate my_crate;

use test::Bencher;
use my_crate::parser::foo;

#[bench]
fn temp(b: &mut Bencher) {
    b.iter(|| { foo(); });
}

我的当前解决方法是将相关对象设置为公共的并在文档中隐藏(#[doc(hidden)]),但这并不能传达正确的意图。我能否仅将一个对象公开以用于集成测试/基准测试目的?


你有在它们上面尝试过 #[cfg(test)] 吗? - Matthieu M.
@MatthieuM。我也需要它们用于非测试目的。 - ljedrz
嗯...不确定是否可能只在“pub”上放置条件,我猜复制定义不是一个选项 :x - Matthieu M.
1
如果使用了我不想在包外部导出的函数,那么这就不是一个集成测试。集成测试的目的是像用户一样运行代码。 - Shepmaster
@Shepmaster 说实话,这在我的基准测试中出现了,我认为它可能是“Bencher”特有的,但我检查了同样的“tests”,结果是相同的。由于后者更容易分析,所以我选择它作为主题。 - ljedrz
2个回答

8
集成测试和单元测试之间的一个区别在于,集成测试只应该测试您的 crate 的“公共 API”。将内部函数的测试与函数本身一起保存在 src 树中是可以的。
如果您想将它们保持有点分离,则可以使用测试子模块来测试包含要测试的函数的模块,因为私有部分对子模块可用。
如果您仍然真的希望在 tests 目录中有内部/单元测试,您可以使用功能标志来启用内部函数的公共包装进行测试(并使用相同的功能标志标记测试)。代码中类似这样的内容:
#[cfg(feature = "testable_privates")]
pub fn exposed_something(args) {
    something_private(args)
}

然后在你的测试方法中,你可以导入并调用 exposed_something。如果特性 testable_privates 没有定义,你的测试将无法编译通过。为了解决这个问题,使用特性标志也让测试条件化;

#[cfg(feature = "testable_privates")]
#[test]
fn test_something() {
    assert_eq!(exposed_something(my_args), expected_result)
}

在执行之前,您需要在Cargo.toml中定义该功能,如下所示:

[features]
testable_privates = []

(空数组表示该特性不需要任何可选依赖项。)
现在,如果你只运行,那么exposed_something和test_something将被默默忽略,但如果你运行,它们将被编译和测试。
如你所见,这变得相当复杂,因此我认为最好的做法是仅从测试您的板条箱的公共方面,然后将私有方法的测试保持靠近这些方法本身,在中。

包装函数(即使只有 #[cfg(test)])是函数的有效解决方案,但对于枚举或结构体来说并不是那么有效。在类似于 pub(specific_external_crate) 的东西出现之前,我想我只需要稍微重新安排一下我的测试和基准就可以了:)。 - ljedrz
这有点不幸。集成测试没有理由不能访问内部函数来验证测试是否成功。这可以使它们更快,更容易编写。忽略 #[cfg(test)] 只会让这变得困难,我看不到任何好处。 :-/ - Timmmm
我认为,如果我们放弃单元/集成术语,从测试的本质出发来看待它们,这是完全有道理的:/src中的测试是_内部_测试,而/tests中的测试是_外部_测试。外部测试只访问您的公共API这一事实是整个区别所在。 - Rasmus Kaj

-1
你可以通过添加一个公共模块来实现,该模块仅在测试时存在,并重新导出所需的符号。类似于以下内容:
#[cfg(test)]
pub mod testing_parser {
    pub use parser::foo;
}

然后在你的测试中使用my_crate::testing_parser::foo


不幸的是,我得到了 error[E0364]: parse is private, and cannot be reexportednote: consider marking parse as pub in the imported module - ljedrz
2
好的,你可以将 parse 标记为 pub,只要将其放在私有模块中,它就不会直接从你的 crate 外部访问,但它可以被重新导出。不幸的是,我刚刚检查了一下,在集成测试中禁用了 #[cfg(test)] 项(即位于 tests 文件夹中的测试),因此您可能需要使用一个 feature 来启用重新导出模块,正如 Rasmus Kaj 建议的那样。 - Jmb

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