【问题标题】:Unit testing copy constructr and assignment operator单元测试复制构造函数和赋值运算符
【发布时间】:2015-03-04 12:41:16
【问题描述】:

我正在为几个类 (C++) 编写单元测试,并在尝试为复制构造函数和赋值运算符编写单元测试时遇到了问题。 两者都可能出错的一个基本问题是程序员向类中添加了一个成员,然后忘记更新 c'ctor 和/或 operator=。

我当然可以按照以下方式编写单元测试:

class MyClass()
{
public:

    int a, b;
    non_trivial_copyable nasty;

    MyClass& operator=(const MyClass& _r)
    {
        if(this == &r)
          return *this;
        a = _r.a;
        b = _r.b;
        nasty = acquire_non_trivial_copyable();
    }
};


TEST(My_Class_copy_op)
{
  MyClass m1;
  m1.a = m1.b = 2;

  MyClass m2 = m1;
  VERIFY(m2.a == 2);
  VERIFY(m2.b == 2);
}

很好。 现在程序员添加了一个成员c,但不更新操作符和测试用例。

class MyClass()
{
public:
    float c;
// ...
}

即使操作符现在坏了,测试用例仍然会成功。

现在,我们可以执行以下操作:

TEST(My_Class_copy_op)
{
// Aha! Fails when programmer forgets to update test case to include checking c
   static_assert(sizeof(MyClass) == 8);
// Meh, also fails on an architecture where the size of MyClass happens to be != 8

   // ...
}

我找不到任何关于如何解决这个问题的好信息,但肯定有人以前遇到过这个问题!? 是不是太明显了,我完全错过了!?

【问题讨论】:

  • 要吸取的教训是,编写不需要的代码是不必要的潜在错误来源。只需删除错误的赋值运算符即可。或者提供一个实际需要的例子。
  • 好吧,课程当然没那么简单。让我们假设有一个成员阻止隐式复制构造函数做正确的事情。例如,假设 MyClass 包含一个互斥体。
  • 好的。然后去问开发人员为什么他们没有先写测试就添加了一个数据成员:-)
  • 我打算建议实施 operator==() 但后来意识到这也会有同样的问题! :P
  • 您似乎在问是否有一种机制可以编写具有超越修改有效性的单元测试。这样的条件似乎消除了对代码审查的需要,而这种分歧会被发现的地方。我不知道您在哪里工作,但在我的工作中,开发人员 负责更新受代码更新影响的单元测试。如果他们不这样做并且测试错误地或通过,那他们的头上。

标签: c++ unit-testing copy-constructor assignment-operator


【解决方案1】:

显式测试一个复制构造函数是好的,但它可能是绕圈子。更常见的是,复制构造函数本身是您不需要显式测试的细节。相反,您可能想要做的是编写一套对您复制的对象起作用的测试,并确保该工作得出正确的结果。像这样:

MyClass a;
// Now initialize a with stuff it needs to do its job

// Copy a into b
MyClass b = a;

// Make b do its job and make sure it succeeds
VERIFY(b.DoWork());

【讨论】:

  • 除此之外,您可以记录a.SomeOperation() 的结果(或副作用)并将其与b.SomeOperation() 进行比较。当然,这仍然只有在单元测试实际测试使用新成员变量的功能时才有效。
  • 对,正如其他发帖人所说,没有什么灵丹妙药可以阻止开发人员更新或编写新的单元测试。
  • 我会接受这个答案,因为这似乎是验证功能的最明智的方式。 @mbgda 我只是想提供一个很好的“提醒”。
  • 另外,“好的提醒”可以是代码覆盖率测试报告。如果代码被更改并且单元测试覆盖的测试代码较少,则需要额外的测试或现有测试的扩展。想象一下,添加了 c 成员,但它从未在类的任何函数中使用...覆盖率不会改变。但是 operator=() 中被遗忘的代码将无法引入新的错误。也许稍后,当代码开始使用 c 时,缺少覆盖会导致添加测试,这会暴露 bug。
  • 最后一点,静态代码检查器是您的朋友。为了获得高质量的代码,仅靠单元测试是不够的。代码覆盖率评估、静态代码检查和检测的组合更接近“理想”。剥夺开发人员这样的机会,然后将其宣布为“人力资源”问题(参见其他一些 cmets)是一条通向黑暗面的道路。
猜你喜欢
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多