单元测试析构函数?
有什么好的方法来单元测试析构函数吗? 就像说我有一个像这样的类(人为的)示例:
class X
{
private:
int *x;
public:
X()
{
x = new int;
}
~X()
{
delete x;
}
int *getX() {return x;}
const int *getX() const {return x;}
};
有没有什么好的方法可以对其进行单元测试,以确保 x 被删除,而不会用 #ifdef 测试弄乱我的 hpp 文件或破坏封装? 我看到的主要问题是很难判断 x 是否真的被删除,特别是因为在调用析构函数时该对象超出了范围。
Is there any good way to unit test destructors? Like say I have a class like this (contrived) example:
class X
{
private:
int *x;
public:
X()
{
x = new int;
}
~X()
{
delete x;
}
int *getX() {return x;}
const int *getX() const {return x;}
};
Is there any good way to unit test this to make sure x gets deleted without cluttering up my hpp file with #ifdef TESTs or breaking encapsulation? The main problem that I'm seeing is that it's difficult to tell if x really got deleted, especially because the object is out of scope at the time the destructor is called.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(7)
我认为你的问题是你当前的示例不可测试。 由于您想知道 x
是否已删除,因此您确实需要能够用模拟替换 x
。 对于 int 来说这可能有点 OTT,但我想在你的真实示例中你还有其他一些类。 为了使其可测试,X
构造函数需要请求实现 int
接口的对象:
template<class T>
class X
{
T *x;
public:
X(T* inx)
: x(inx)
{
}
// etc
};
现在模拟 x
的值变得很简单>,并且模拟可以处理正确销毁的检查。
请不要理会那些说你应该破坏封装或诉诸可怕的黑客以获得可测试代码的人。 虽然经过测试的代码确实比未经测试的代码更好,但可测试的代码是最好的,它总是会导致更清晰的代码、更少的黑客攻击和更低的耦合。
我倾向于采用“通过任何必要的手段”进行测试。 如果需要测试,我愿意泄漏抽象、破坏封装和破解……因为经过测试的代码比漂亮的代码更好。 我经常将破坏这种情况的方法命名为 VaildateForTesting 或 OverrideForTesting 之类的名称,以明确破坏封装仅用于测试。
除了让析构函数调用单例来注册它已被销毁之外,我不知道在 C++ 中还有其他方法可以做到这一点。 我想出了一种方法,可以使用弱引用在 C# 中执行类似的操作(我不会违反这种方法的封装或抽象)。 我没有足够的创造力来与 C++ 进行类比,但你可能会。 如果有帮助,那就太好了,如果没有,抱歉。
http:// /houseofbilz.com/archive/2008/11/11/writing-tests-to-catch-memory-leaks-in-.net.aspx
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
对于依赖注入可能还有一些话要说。 该对象不是在其构造函数中创建对象(在本例中是 int,但在非人为的情况下更可能是用户定义的类型),而是作为参数传递给构造函数。 如果稍后创建对象,则将工厂传递给 X 的构造函数。
然后,当您进行单元测试时,您传入一个模拟对象(或创建模拟对象的模拟工厂),析构函数会记录以下事实:它已被称为。 如果不是,则测试失败。
当然,您不能模拟(或以其他方式替换)内置类型,因此在这种特殊情况下它不好,但如果您使用接口定义对象/工厂,那么您可以。
正如其他人所说,单元测试中的内存泄漏检查通常可以在更高的级别上完成。 但这仅检查a析构函数被调用,并不能证明正确析构函数被调用。 因此,它不会在 x 成员类型的析构函数上捕获丢失的“虚拟”声明(同样,如果它只是一个 int,则不相关)。
There may be something to be said for dependency injection. Instead of creating an object (in this case an int, but in a non-contrived case more likely a user-defined type) in its constructor, the object is passed as a parameter to the constructor. If the object is created later, then a factory is passed to the constructor of X.
Then when you're unit testing, you pass in a mock object (or a mock factory which creates mock objects), and the destructor records the fact that it has been called. The test fails if it isn't.
Of course you can't mock (or otherwise replace) a builtin type, so in this particular case it's no good, but if you define the object/factory with an interface then you can.
Checking for memory leaks in unit tests can often be done at a higher level, as others have said. But that only checks that a destructor was called, it doesn't prove that the right destructor was called. So it wouldn't e.g. catch a missing "virtual" declaration on the destructor of the type of the x member (again, not relevant if it's just an int).