是否可以使用 MSTest 从 c++ dll 测试"internal"类?



我们正在尝试将单元测试添加到我们的c++应用程序中。该应用程序由30个项目组成,生成29个dll和1个exe。我们使用MSTest来运行我们的单元测试,因为它已经包含在Visual Studio 2010中。

对于声明为"public"的类非常有效。这些类开头是这样的:

#ifdef RESEAU_IMPL
    #define CLASS_DECL      _declspec(dllexport)
#else
    #define CLASS_DECL      _declspec(dllimport)
#endif 

但是对于所有其他类(90%的代码),它们没有被声明为public,所以我们不能在测试中使用它们。

我在谷歌上读到过InternalVisibleTo属性,但它似乎只适用于c# . net程序集。我说的对吗?我也读到声明我的类"as_friend",但我不确定把它放在哪里。

所以简单地说:我想测试那些没有在DLL中导出/公开的类。我怎么做呢?

感谢

* EDIT *

Gishu评论说单元测试在非托管代码中是不可能的,但它是可能的。看,这是一个测试本地c++代码的testmethod。CVersion在c++ MFC中。

[TestMethod]
void AssignationCVersion()
{
    CVersion version1234(1,2,3,4);
    CVersion version4321(4,3,2,1);
    Assert::IsTrue(version1234 != version4321);
    version1234 = version4321;
    Assert::IsTrue(version1234 == version4321);
};

但是似乎不可能使用特殊的标签来测试内部函数。我是第一个同意测试内部方法不是好的做法,但这些DLL不是实用程序函数,而是"真正"应用程序的一部分(也许这是糟糕的设计,但它是15年前完成的)。有人对这个话题有什么想法吗?

参见question:在DLL中对非导出类进行单元测试

三个选项如下:

  • 将测试代码放入DLL中,以便它可以访问未导出的类和函数
  • 将包含测试代码的所有文件添加到测试项目中,以便它们被编译两次(不知道这是否适用于MSTEst,但这将是您使用Boost test或CPPunit之类的东西进行编译的方式)
  • 将所有未导出的可测试代码构建到一个静态库中,然后链接到测试代码和DLL。

这些都有不同的问题。

将测试代码放入DLL中并不理想。要么您只将它包含在非生产构建中,在这种情况下,您没有测试您发布的内容,要么您将它包含在所有构建中,在这种情况下,您正在发布测试代码,这可能是不希望看到的。也有然后需要某种形式的入口点来访问这些测试,从而迫使编译器包括的所有代码,阻止优化器删除它如果它原本被视为无法访问(可能无法访问的一些代码的测试从任何公共方法的DLL,所以优化器可以决定删除它们是死代码,测试的DLL阻止)。

将源文件添加到两个项目中会增加构建时间和维护复杂性。每次添加新的源文件或删除源文件时,都需要在两个位置添加。这也取决于代码的大小,这会大大增加构建时间,因为它必须构建许多代码两次。

将所有未导出的可测试代码放入静态库的缺点是在解决方案中创建额外的项目,并使组织更加复杂。您需要小心代码结构(例如,一个源文件应该只包含导出或非导出的代码),并且这意味着您需要为导出部分和非导出部分单独的测试项目。然而,这意味着代码只编译一次,测试不是最终可执行文件的一部分,优化器可以完成它的全部工作。

根据DLL的公共接口的大小,也就是导出的类/函数的数量,我认为第三种选择是最可行的。通常你只有一个小的公共接口,这是一个更大的内部结构的门面。除了公共外观之外的所有内容都可以放入单独的静态库中,然后可以轻松地将其链接到测试可执行文件和DLL。

无论你是一个单元测试框架还是其他什么东西,都没有办法测试你看不到的代码。Windows上的DLL只导出定义了__declspec(dllexport)的符号。当编译DLL 时,任何其他符号都被视为内部,并且对使用DLL的代码不可见。

这很重要,因为它意味着链接器可以优化、修改或删除未导出的代码。要测试的代码可能根本不存在。它可能在那里,但以不同于你想象的形式存在。DLL是在一个契约下编译的,用dllexport声明的任何东西都必须是存在的和可见的,其他任何东西都必须工作。它不需要从外部世界访问。

这不是MSTest的缺点(尽管它有很多其他缺点,对于c++代码的单元测试来说是一个非常糟糕的选择)

如果你想测试代码,你有两个选择:

  • dllexport导出,或
  • 将单元测试代码编写为dll本身的一部分。

别打信使。

  • Visual Studio单元测试(也就是使用MSTest.exe运行的测试)仅测试托管代码。您不能测试非托管c++。在VS11(下一个版本)中有一个新的本机单元测试框架。
  • InternalsVisibleTo像你说的也只适用于托管代码。
  • 你通常不需要测试内部类。就像私有类型或方法一样,您可以通过使用它们的公共/公开方法来测试它们。因此,如果PublicA.Method1()是您的客户端执行InternalHelper.Method2()的方式;然后我依靠PublicA.Method1()的测试来告诉我它们中的任何一个是否损坏。
  • 如果你必须测试内部类,试着把它们变成公共的(如果方法足够复杂的话)。参见MethodObject重构)。或者您可以使测试类成为内部类的朋友。

.

class ProductionSUT
{
  // production code to be tested
  friend class TestProductSUT;
}

免责声明:我还没有试过这个…所以可能需要一些调整来安抚编译器

最新更新