我该把单元测试源码放在哪里并暴露内部组件?

3
我正在接手一个通过ATL公开组件的项目。
我看到这种设置需要进行两个主要领域的单元测试:
1. 测试内部组件(可能会通过COM公开或不公开); 2. 测试外部公开的组件(即测试公开接口)。
目前,该项目的所有内部组件单元测试都在解决方案中。通过预处理器标志启用它们,并在运行时执行。
从我所做的研究来看,似乎“惯例”是将单元测试放置在不同的子项目中,并让主解决方案提供钩子以便单元测试可以访问内部组件。使用这种设置,单元测试解决方案将对待测试的解决方案产生依赖。这真是“惯例”,还是有很多人将他们的单元测试框架放在被测试的解决方案中(即单元测试不是子项目,而是松散的cpp文件,如果未提供预处理器标志,则将其排除在外)?
当前使用的单元测试框架似乎是cppunit,我正在考虑将其切换到gtest并尝试将一切移动到不同的子项目中,但我想确保长期而言这个努力值得。其中一种方法是使用__declspec声明要测试的类,只有在指定了预处理器定义时才公开它们。然后,单独的单元测试子项目将启用该预处理器,告诉主解决方案公开内部组件。我只是不确定这是否是最佳路线。
所以我的问题是:
1. 惯例是将单元测试放在一个单独的(子)项目中,并从将要测试的源代码中公开组件(通过钩子、公开类定义等)吗? 2. 从COM DLL中公开内部组件的最佳方法是什么? 3. 启用__declspec来测试内部组件的预处理器标志是个坏主意吗?其他人是否使用过这种方式进行单元测试,其中被测试的项在正常操作期间通常不公开?
感谢您的评论!

UnitTest++也是一个非常愉悦的选择。 - Tom Kerr
3个回答

4
另一团队的某人表示,他们的团队将所有单元测试放在独立的项目中,并通过静态库公开了内部内容。这对我来说很有道理,因为它有助于实现模块化并提供许多其他好处。因此,每个代码的主要耦合都将是一个静态库(不会过分);对于每个静态库,您都需要有一个单独的单元测试解决方案。然后,您的主DLL成为公开必要项的薄包装器。
所以,解决方案如下:
MainSolution
- ProjCommonLib - ProjExportsLib - ProjImportsLib - ProjOtherStuffLib - COMDLL-Proj - UnitTestsFolder - ProjCommonLibTests - ProjExportsLibTests - ProjImportsLibTests - ProjOtherStuffLibTests - COMDLL-ProjTests
这种设置允许单元测试设置依赖关系,并完全访问内部,而无需处理__declspec。这似乎更加清晰,因为DLL没有任何性能损失,因为其他库只是静态库。
我将其放置为“答案”,以查看是否获得任何点赞(即,“此答案是一个好方法,也是我所做的”)。如果这是一个不好的解决方案,请发布您的答案。

4
好的方法,这也是我所做的。当我不使用静态库来包含代码时,在单元测试项目中,我会将$(SolutionDir)$(ProjectName)Debug\obj文件夹添加到链接器的附加库目录中。 - John Deters
那很聪明。我也得试试。 - BabelFish

1

我认为你可以自由选择是将测试嵌入到现有项目中还是创建一个新项目。对于我的cppunit测试,我选择前者的方法,因为这意味着你不需要创建新项目,但对于C#项目,我使用Visual Studio的单元测试框架来创建和维护一个单独的项目。

我从未遇到过我的#ifdef块没有重新编译的问题--依赖检查会处理这个问题(如果你将它们暴露在一个单独的dll中,然后使用exports,那么这将是一个问题)。

我已经使用cppunit很多年了,最近才开始使用gtest。我认为虽然gtest功能非常丰富,但有相当多的结构差异会使移植成为一项艰巨的工作。如果现有的测试是在cppunit中,则建议继续使用它。

我喜欢cppunit的一件事情(据我所知,gtest不支持),就是fixture中的第一个失败会导致整个fixture失败,所以你可以这样做:

CPPUNIT_ASSERT(pointer!=NULL);
CPPUNIT_ASSERT(pointer->deferenceIt());  // if the pointer was null it would have returned above

gtest将在此实例中继续运行,并在第二行崩溃。如果cppunit测试依赖于此行为,则这将使移植变得更加困难。


我知道gtest的“出错后继续执行”功能,但从未考虑过其缺点。你说得很有道理。 - BabelFish
此外,我公司另一个团队的某位成员提到,转换到 gtest 可能比它值得的工作还要多,建议我继续使用 cppunit。很高兴听到其他人也持同样的看法。 - BabelFish
我同意。我喜欢gtest,如果有现有的cppunit测试套件,我会坚持使用它,但对于新项目,我会考虑使用gtest。 - the_mandrill
注意 ASSERT 和 EXPECT 的区别。当使用 ASSERT_ 类的验证宏时,测试会在第一个错误处停止。而 EXPECT_ 系列则会在出现断言错误后继续执行。 - Kjell Gunnar

0

使用#ifdef块可能不会触发重新编译,因此我认为将单元测试放在另一个项目中是更好的选择。


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