在没有朋友的情况下在 C++ 中测试私有类成员[重复]

2024-02-26

今天和同事讨论了课堂上要不要测试私有成员或者私有状态。他几乎说服了我为什么这是有道理的。这个问题的目的并不是重复 StackOverflow 已经存在的有关测试私有成员的性质和原因的问题,例如:让单元测试成为它正在测试的类的友元有什么问题吗? https://stackoverflow.com/questions/4171310/what-is-wrong-with-making-a-unit-test-a-friend-of-the-class-it-is-testing

在我看来,同事的建议是将友元声明引入到单元测试实现类中有点脆弱。在我看来这是不行的,因为我们引入了被测试代码对测试代码的一些依赖,而测试代码已经依赖于被测试代码=>循环依赖。即使是像重命名测试类这样无辜的事情也会导致破坏单元测试并强制测试代码中的代码更改。

我想请 C++ 专家对另一个提案进行判断,该提案依赖于我们可以专门化模板函数这一事实。想象一下这个班级:

// tested_class.h

struct tested_class 
{
  tested_class(int i) : i_(i) {}

  //some function which do complex things with i
  // and sometimes return a result

private:
  int i_;
};

我不喜欢为 i_ 设置 getter 只是为了使其可测试。所以我的建议是在类中声明“test_backdoor”函数模板:

// tested_class.h

struct tested_class 
{
  explicit
  tested_class(int i=0) : i_(i) {}

  template<class Ctx>
  static void test_backdoor(Ctx& ctx);

  //some function which do complex things with i
  // and sometimes return a result

private:
  int i_;
};

通过添加这个函数,我们可以使类的私有成员可测试。请注意,不依赖于单元测试类,也不依赖于模板函数实现。在此示例中,单元测试实现使用 Boost Test 框架。

// tested_class_test.cpp

namespace
{
  struct ctor_test_context
  {
    tested_class& tc_;
    int expected_i;
  };
}

// specialize the template member to do the rest of the test
template<>
void tested_class::test_backdoor<ctor_test_context>(ctor_test_context& ctx)
{
  BOOST_REQUIRE_EQUAL(ctx.expected_i, tc_.i_);
}

BOOST_AUTO_TEST_CASE(tested_class_default_ctor)
{
  tested_class tc;
  ctor_test_context ctx = { tc, 0 };
  tested_class::test_backdoor(ctx);
}

BOOST_AUTO_TEST_CASE(tested_class_value_init_ctor)
{
  tested_class tc(-5);
  ctor_test_context ctx = { tc, -5 };
  tested_class::test_backdoor(ctx);
}

通过仅引入一个根本不可调用的模板声明,我们为测试实现者提供了将测试逻辑转发到函数中的可能性。该函数作用于类型安全上下文,并且由于测试上下文的匿名类型性质,仅在特定测试编译单元内部可见。最好的事情是,我们可以定义任意数量的匿名测试上下文,并对它们进行专门的测试,而无需触及被测试的类。

当然,用户必须知道什么是模板专业化,但是这段代码真的很糟糕、很奇怪或不可读吗?或者我是否可以期望 C++ 开发人员了解什么是 C++ 模板专业化及其工作原理?

详细说明使用朋友声明单元测试类我认为这并不可靠。想象一下 boost 框架(或者可能是其他测试框架)。它为每个测试用例生成一个单独的类型。但只要我能写,我为什么要关心:

BOOST_AUTO_TEST_CASE(tested_class_value_init_ctor)
{
  ...
}

如果使用朋友,我必须将每个测试用例声明为朋友...或者最终在某些常见类型(如固定装置)中引入一些测试功能,将其声明为朋友,并将所有测试调用转发到该类型。 . 这不是很奇怪吗?

我想看看您在实践这种方法时的优点和缺点。


我认为单元测试是关于测试可观察到的被测试类的行为。因此,没有必要测试私人部分,因为它们本身是不可观察的。测试它的方法是测试对象是否按照您期望的方式运行(这隐含地暗示所有私有内部状态都是有序的)。

不关心私有部分的原因是这样你可以改变实现(例如重构),而不必重写你的测试。

所以我的答案是不要这样做(即使技术上可行),因为它违背了单元测试的理念。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在没有朋友的情况下在 C++ 中测试私有类成员[重复] 的相关文章

  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 秒表有最长运行时间吗?

    多久可以Stopwatch在 NET 中运行 如果达到该限制 它会回绕到负数还是从 0 重新开始 Stopwatch Elapsed返回一个TimeSpan From MSDN https learn microsoft com en us
  • 用于检查类是否具有运算符/成员的 C++ 类型特征[重复]

    这个问题在这里已经有答案了 可能的重复 是否可以编写一个 C 模板来检查函数是否存在 https stackoverflow com questions 257288 is it possible to write a c template
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • 使用实体框架模型输入安全密钥

    这是我今天的完美想法 Entity Framework 中的强类型 ID 动机 比较 ModelTypeA ID 和 ModelTypeB ID 总是 至少几乎 错误 为什么编译时不处理它 如果您使用每个请求示例 DbContext 那么很
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 重载<<的返回值

    include
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • solr 不标记受保护的单词

    我在 Solr Lucene 3 x 中有一个文档 其中有一个特殊的复制字段facet headline 以便有一个用于分面的未词干字段 有时两个或以上的单词属于在一起 这应该被处理 算作一个单词 例如 kim jong il 因此标题 星
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐