在没有朋友的情况下在 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++ 中测试私有类成员[重复] 的相关文章

  • 采用 std::vector 或 std::array 的模板函数

    我有一个函数 当前接受 2 个向量 其中可以包含任何普通的旧数据 template
  • 信号与信号2

    我的应用程序可能会受益于使用 boost 的信号库之一而不是本土解决方案 该应用程序是多线程的 但执行信号处理的部分是单线程的 如果多线程不是问题 是否有任何理由更喜欢 Boost Signals2 而不是 Boost Signal Boo
  • Qt/c++ 随机字符串生成[重复]

    这个问题在这里已经有答案了 我正在创建一个应用程序 需要生成多个随机字符串 几乎就像一个由一定长度的 ASCII 字符组成的唯一 ID 这些字符混合有大写 小写 数字字符 有没有 Qt 库可以实现这一点 如果没有 在纯 C 中生成多个随机字
  • Winform DatagridView 数字列排序

    我只使用一个简单的 DataGridView 来保存一堆数据 有趣的是 我在特定列中有小数 但是当按小数列排序时 它的排序是错误的 例如 起始顺序可能是 0 56 3 45 500 89 20078 90 1 56 100 29 2 39
  • 对 ExecuteNonQuery() 的单次调用是原子的

    对 ExecuteNonQuery 的单次调用是否是原子的 或者如果单个 DbCommand 中有多个 sql 语句 那么使用事务是否有意义 请参阅我的示例以进行说明 using var ts new TransactionScope us
  • 无法将参数从 `const char *` 转换为 `char *`

    鉴于此代码 void group build int size std string ips Build the LL after receiving the member list from bootstrap head new memb
  • 为什么 BinaryFormatter 可以序列化 Action<> 但 Json.net 不能

    尝试序列化 反序列化 Action 尝试我的 1天真 JsonConvert SerializeObject myAction JsonConvert Deserialize
  • 模板与非模板类,跨编译器的不同行为

    我在一些应用程序中使用编译时计数器 它确实很有用 昨天我想用 gcc 编译一个程序 我之前使用的是 msvc 并且计数器的行为在模板类中发生了变化 它在模板类中不再工作 过于简化的代码 Maximum value the counter c
  • 如何在 C++ 运行时更改 QML 对象的属性?

    我想在运行时更改 QML 对象的文本 我尝试如下 但文本仍然为空 这是后端类 class BackEnd public QObject Q OBJECT Q PROPERTY QString userFieldText READ userF
  • 本地时间的内存需要释放吗?

    void log time t current time 0 tm ptm localtime current stuf 只是想确定 我是否需要在方法结束时释放 tm 指针分配的内存 不 你不应该释放它 该结构是静态分配的 检查文档 htt
  • 在 C# .NET 中对非 ASCII 字符进行编码

    我想向我的应用程序发送的电子邮件添加自定义标头 标头名称只能包含 ASCII 字符 但对于值和用户可能会输入 UTF 8 字符 我必须对它们进行 Base64 编码 此外 我还必须将它们解码回 UTF 8 以便在 UI 中向用户显示它们 最
  • 使用 Maven 插件测试工具测试 Mojos 的默认值和表达式:

    我在使用 Maven 插件测试工具 2 0 alpha1 时遇到问题 当我想测试 Mojo 时 参数的默认值和表达式不适用 我有以下参数 p The output file to write the settings to p parame
  • 调试错误:在 vc++ 项目中使用 COM 时发生 所需的运行时?

    我为我的工作创建了一个 COM 组件 我也注册了该组件 在我的系统上 我有两个虚拟机工作站 在我的第一个工作站中 它运行良好 在我的第二个工作站中 它显示一个包含消息的错误框该程序需要一段时间并以不寻常的方式关闭 请联系应用程序管理员 我认
  • 如何释放字符串未使用的容量

    我正在程序中处理很多字符串 这些字符串数据在读入我的程序后的整个生命周期内都不会改变 但由于 C 字符串保留了容量 因此浪费了大量肯定不会被使用的空间 我尝试释放这些空间 但没有成功 以下是我尝试过的简单代码 string temp 123
  • 如果仅使用第一个元素,是否必须为整个结构分配内存?

    我有一个结构 其中第一个元素被测试 并且根据其值 结构的其余部分将被读取或不会被读取 在第一个元素的值指示结构的其余部分不会被读取的情况下 我是否必须为整个结构或仅第一个元素分配足够的内存 struct element int x int
  • 'iter' 的名称查找已更改为新的 ISO 'for' 范围

    我正在尝试编译下面的两个文件 但从编译器收到错误消息 gcc 4 3 3 Linux 错误位于带有以下符号的行 LINE WITH ERROR 我做错了什么 我该怎么改变 路易斯 g c b h b cpp b cpp In functio
  • 如何在控制台程序中获取鼠标位置?

    如何在 Windows 控制台程序中用 C 获取鼠标单击位置 点击时返回鼠标位置的变量 我想用简单的文本命令绘制一个菜单 这样当有人点击时 游戏就会注册它并知道位置 我知道如何做我需要做的一切 除了单击时获取鼠标位置 您需要使用 Conso
  • 使用C标准数学库精确计算标准正态分布的CDF

    标准 C 数学库不提供计算标准正态分布 CDF 的函数 normcdf 然而 它确实提供了密切相关的函数 误差函数 erf 和互补误差函数 erfc 计算 CDF 的最快方法通常是通过误差函数 使用预定义常量 M SQRT1 2 来表示 d
  • C# PasswordDeriveBytes:似乎 Salt 并不重要

    可能我误解了什么 以下代码通过 CryptDeriveKey 使用两种不同的盐生成两个相等的密钥 这是控制台结果 盐1 21 3e 18 a3 9a 8b 5f gt 键 da 89 ea 3d 91 08 20 98 20 e9 dc 4
  • 如何通过API退出Win32应用程序?

    我有一个使用 Win32 API 编写的 C Win32 应用程序 我希望强制它在其中一个函数中退出 有没有类似的东西Exit or Destroy or Abort 类似的东西会终止它吗 哎呀呀呀呀呀呀 不要做任何这些事情 exit 和

随机推荐