注意:我知道active_
在我的例子中可能是“任何东西”。这不是这个问题的内容。它是关于使“未定义的值”可靠地通过单元测试失败。
编辑:从“无构造函数”更改为“空构造函数”。
我正在开发一个C ++类,我正在使用TDD。现在我想确保正确初始化bool
类成员 - 在构造函数中为其分配值。所以我编写了以下测试(使用Google Mock / Google Test框架):
TEST(MyClass, isNotActiveUponCreation) {
MyClass my;
ASSERT_FALSE(my.isActive());
}
以下类定义:
class MyClass {
public:
// Note: Constructor doesn't initialize active_
MyClass() {}
bool isActive() const { return active_; }
private:
bool active_;
};
问题:在我的机器上,即使active_
从未初始化,此测试也会始终通过。现在我们知道active_
的值是未定义的,因为它是一个原始类型并且从未初始化。所以从理论上讲,它可能在某些时候true
,但最终,不可能知道。最重要的是,我无法使用这种方法可靠地测试缺少的初始化。
有没有人知道如何以确定性和可重复的方式测试这种情况?或者我必须忍受它,省略这种测试,并希望我永远不会忘记初始化一个布尔成员,或者其他测试将始终捕获产生的缺陷?
答案 0 :(得分:3)
在阅读了TobiMcNamobi的回答后,我记得placement new,并了解了如何解决我的问题。除非我在构造函数中初始化active_
,否则以下测试可靠地失败:
#include <gmock/gmock.h>
#include <vector>
class MyClass {
public:
// Note: Constructor doesn't initialize active_
MyClass() {}
bool isActive() const { return active_; }
private:
bool active_;
};
TEST(MyClass, isNotActiveUponCreation) {
// Memory with well-known content
std::vector<char> preFilledMemory(sizeof(MyClass), 1);
// Create a MyClass object in that memory area using placement new
auto* myObject = new(preFilledMemory.data()) MyClass();
ASSERT_FALSE(myObject->isActive());
myObject->~MyClass();
}
现在我承认这个测试不是最可读的,并且可能不会立即清楚,但它可靠地运行并独立于任何第三方工具,如valgrind。是否值得额外的努力?我不确定。它在很大程度上取决于MyClass
内部,这将使它非常脆弱。无论如何,它是在C ++中测试正确初始化对象的一种方法。
答案 1 :(得分:1)
一旦你进行了单元测试,这类问题实际上非常容易进行单元测试。
只需在内存检查器下运行单元测试(linux上的valgrind,不确定Windows上使用的是什么)。
我没有创建gtest可执行文件,而是创建了一个简单的例子:
#include <iostream>
class MyClass {
public:
// Note: no constructor
bool isActive() const { return active_; }
private:
bool active_;
};
int main()
{
MyClass c; // line 17
std::cout << c.isActive() << std::endl;
}
在valgrind下运行它,我得到了下一个输出(修剪不需要的行):
==9217==
==9217== Conditional jump or move depends on uninitialised value(s)
.....
==9217== by 0x40094F: main (garbage.cpp:17)
使用valgrind执行单元测试时,您将遇到与内存访问相关的所有问题。你也会得到回溯。
答案 2 :(得分:1)
我的小测试:
#include <stdlib.h>
#include <vector>
#include <iostream>
class MyClass {
public:
// Note: Constructor doesn't initialize active_
MyClass() {}
bool isActive() const { return active_; }
private:
bool active_;
};
int main(int argc, char* argv[])
{
std::vector<MyClass> vec(1000);
for (int i = 0; i < 1000; i++)
{
std::cout << (vec[i].isActive() ? "1" : "0");
}
return system("pause");
}
那么执行此操作时会发生什么(使用VS2012编译)?
调试配置:写入1千字节。
发布配置:写入0 0。我把迭代次数提高到了10万,并得到了十万0 ...等等,前几个数字是10101110000000 ......那里!在这样的另一个序列之间的某处是隐藏的。
这是什么意思?结果或多或少是预期的。您无法预测单个未初始化位的设置方式。你想在这里做的是初始化内存中的一些空间并在那里创建一个对象,就好像那个内存之前没有初始化一样。
所以,直到我被证明是错误的:你无法对它进行单元测试。
除非您使用工具(例如valgrind,请参阅其他答案)。
我认为TDD很棒,但它当然有其局限性。我只是初始化标志并继续红绿重构循环。答案 3 :(得分:1)
TDD测试应该行使行为而不是实施细节。构造函数初始化,setter初始化等的测试取决于具体的实现,如果实现被重构,则会很脆弱。
答案 4 :(得分:0)
你要做的是对未定义的行为进行单元测试,这是毫无意义的,因为显然需要接受所有结果。
答案 5 :(得分:0)
如果使用MemorySanitizer编译单元测试套件,那么从未初始化的内存中读取任何内容都会导致测试失败。