【问题标题】:Is it possible to test "internal" class from a c++ dll using MSTest?是否可以使用 MSTest 从 c++ dll 测试“内部”类?
【发布时间】:2011-10-13 20:02:44
【问题描述】:

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

它适用于声明为“public”的类。这些类的开头是这样的:

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

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

我在 google 上阅读了有关 InternalVisibleTo 属性的信息,但它似乎只适用于 c# .NET 程序集。我对吗?我还阅读了声明我的班级“as_friend”,但我不确定该放在哪里。

简而言之:我想测试 DLL 中未导出/公开的类。我该怎么做?

谢谢

* 编辑 *

Gishu 评论说单元测试在非托管代码中是不可能的,但它是可能的。看,这是一个测试原生 c++ 代码的 TestMethode。 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 年前完成的)。有人对这个主题有想法吗?

【问题讨论】:

  • C++ 不支持反射。你不能测试你不能调用的代码。
  • 您是否使用托管 C++ 测试 dll 调用非托管 SUT?这是我第一次听说这种用途。也找不到任何有关此的 msdn 文档。我工作场所的人们使用 google 的 fwk 或 cppunit ......但如果你正在寻找 IDE 集成和合作,那就不行了。
  • 嗨,这是我们测试“架构”所依据的文档:msdn.microsoft.com/en-us/library/ms243171.aspx。如您所见,它被标记为“不支持”,但他们提供了一种方法并且它有效。这个 MSdn 页面非常具有误导性。

标签: c++ unit-testing mstest


【解决方案1】:

另见问题: Unit testing non-exported classes in a DLL

这三个选项似乎是:

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

这些都有不同的问题。

将测试代码放入 DLL 并不理想。要么只将它包含在非生产构建中,在这种情况下,您不会测试您发布的内容,或者您​​将它包含在所有构建中,在这种情况下,您正在交付测试代码,这可能是不可取的。然后还需要某种入口点来访问这些测试,从而强制编译器包含所有代码,防止优化器在它被认为不可访问时将其删除(可能是您的某些代码无法从 DLL 中的任何公共方法访问正在测试,因此优化器可以决定将它们作为死代码删除——在 DLL 中进行测试可以防止这种情况发生。

将源文件添加到两个项目会增加构建时间和维护复杂性。每次添加新源文件或删除源文件时,都需要在这两个位置添加。此外,根据代码的大小,这可能会大大增加构建时间,因为它必须两次构建大量代码。

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

根据 DLL 的公共接口的大小,即导出的类/函数的数量,我认为第三个选项是最可行的。通常你只有一个小的公共接口,它是一个更大的内部结构的外观。除了公共门面之外的所有东西都可以进入一个单独的静态库,然后可以很容易地链接到测试可执行文件和 DLL。

【讨论】:

  • 感谢您的信息。我们最终选择在需要测试的类前面添加 class_decl。因为无论如何我们永远不会测试所有这些应用程序(旧版软件),所以我们只是在需要时向公共界面显示我们想要测试的内容。
  • 为什么您的第三个解决方案需要 2 个单独的测试项目?在我看来,只需一个就足够了,链接到您的静态库(包含导出的类和非导出的类)(例如,聚合所有 .obj 文件,无论是否导出)。
  • 第二个选项的变体避免重新编译所有源文件。在链接器选项中,将源项目的 $(IntDir) 添加到附加库目录并将 .obj 文件添加到附加依赖项。不要忘记从测试项目到源项目的项目依赖关系。当然,维护复杂性增加了,但只是在添加单元测试时增加了通常的程度(即我需要考虑拉入什么对象来满足测试)。
  • 第三种情况,为什么我们需要将目标代码(待测试)构建为静态库而不是DLL?
【解决方案2】:

无论您是单元测试框架还是其他人,都无法测试您看不到的代码。 Windows 上的 DLL 仅导出定义了 __declspec(dllexport) 的符号。任何其他符号在编译 DLL 时被视为内部,并且不会对使用 DLL 的代码可见。

这很重要,因为它意味着链接器可以优化、修改或删除未导出的代码。您要测试的代码可能根本不存在。它可能在那里,但形式与您预期的不同。 DLL 是根据使用dllexport 声明的任何内容必须存在且可见的合同编译的,其他任何内容都必须工作。它不必从外部世界访问。

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

如果您想测试该代码,您有两种选择:

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

【讨论】:

  • 如何将单元测试作为 dll 本身的“一部分”编写?如果 MSTest 是一个糟糕的选择,您有什么更好的建议?
【解决方案3】:

好吧,不要向信使开枪。

  • Visual Studio 单元测试(即使用 MSTest.exe 运行的测试)仅测试托管代码。您无法测试非托管 C++。 VS11(下一个版本)附带了一个新的原生单元测试框架。
  • InternalsVisibleTo 就像您所说的,也仅适用于托管代码。
  • 恕我直言,您通常不需要测试内部类。就像私有类型或方法一样,您可以通过使用它们的公共/公开方法来测试它们。因此,如果 PublicA.Method1() 是您的客户行使 InternalHelper.Method2() 的方式;然后我依靠 PublicA.Method1() 的测试来告诉我它们中的任何一个是否损坏。
  • 如果您必须测试内部类,请尝试将它们公开(如果方法足够复杂。请参阅 MethodObject 重构)。或者您可以将内部类的测试类设为a friend

.

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

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

【讨论】:

  • 你“不需要测试内部类”。真的吗?
  • @TomQuarendon 是的。考虑到客户端可以访问该代码的唯一方法是通过其他类,测试应该按照相同的路线进行。内部类是一个实现细节。您可以丢弃 InternalClass1 并编写 InternalClass2 代码,只要您保留公开的公共客户端类型的行为,您的测试就不会改变。
猜你喜欢
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多