【发布时间】:2016-10-07 13:15:50
【问题描述】:
我最近开始使用 Catch 和 Fakeit 对我的代码进行单元测试。 我有一个围绕 WinAPIs HWND 的包装类。
class Window
{
public:
Window(HWND hwnd);
virtual void resize(int width, int height);
...
private:
HWND m_hwnd;
};
这和它的测试工作正常。对于测试,我正在创建一些实际的 windows 使用 WinAPI 的 CreateWindow(...)。
但是,我偶然发现了一个问题,我不确定最佳解决方案是什么。 在我的代码中,我只是按值传递 Window,因为它基本上只是 HWND。 有些方法把它当作const&,但是当它作为类成员时,我通常只是复制它。
class Foo
{
public:
Foo(const Window& window)
: m_window(window)
{}
private:
Window m_window;
};
现在假设我想测试 Foo。我需要以某种方式存根 Window 类,但我只能这样做 如果我能够覆盖虚拟方法。我想你在这里看到了问题。
class Foo
{
public:
Foo(const std::shared_ptr<Window>& window)
: m_window(window)
{
assert(m_window != nullptr);
}
private:
std::shared_ptr<Window> m_window;
};
我现在花了最后两个小时,重构我所有的代码,所以我只通过 std::shared_ptr 和几乎 永远不要按值使用 Window 类。 Foo 现在看起来像这样: 班级 这是有道理的,因为即使我按值传递 Window,它通常也是共享的 无论如何(如果我调整它的大小,它会为所有实例调整大小)。 但是,我也觉得它使我的代码复杂了很多。对于比较运算符 == 我现在总是要取消引用 双方。当我试图在 stl 容器中查找时,我现在必须使用
std::find_if(haystack.begin(), haystack.end(),[&needle](const SharedWindowPtr& ptr) { return *ptr == *needle; });
除此之外,还有来自 shared_ptr 的开销。我还冒着传递 nullptrs 的风险,我想确保不会发生这种情况。我可以用断言来检查,但这并不是万无一失的。我也不能传递 shared_ptr 而是传递对 Foo 的构造函数的引用,并为 Window 类提供一个 std::unique_ptr<Window> clone() 方法,但我必须为每个测试模拟它,如果有办法解决它,我更愿意这样做。
现在我想知道是否有更好/更清洁的方法来处理此类问题?感谢您对此提出任何建议。
编辑:在考虑了一段时间后,我有了另一个想法来解决这个问题。
class WindowHandler
{
public:
virtual void resize(int w, int h) = 0;
virtual void getTitle() = 0;
// ...
};
class DefaultWindowHandler
{
public:
DefaultWindowHandler(HWND hwnd);
virtual void resize(int w, int h) override
{
// ...
}
// ...
protected:
HWND m_hwnd;
};
class Window
{
public:
Window(HWND hwnd)
: m_windowHandler(new DefaultWindowHandler(hwnd))
{
// empty
}
void setWindowHandler(WindowHandler* handler)
{
assert(handler != nullptr);
m_windowHandler.reset(handler);
}
void resize(int w, int h)
{
m_windowHandler->resize(w, h);
}
};
我喜欢这个的一点是,我可以通过值传递 Window 类,并且如果需要的话,仍然可以获得类似接口的行为的好处。对此你怎么看,有什么不足之处?我知道 Window 可能应该接受 WindowHandler 而不是 HWND 作为构造函数参数,但使用它的方式更容易。
【问题讨论】:
-
只要
Foo不是Window的所有者,简单的引用或非拥有的原始指针都可以。如果您需要Foo成为所有者,您可以使用std::unique_ptr -
我知道我当然可以使用 unique_ptr ,但这并不能简化任何事情,如果这会让事情变得更复杂的话。我也知道我可以使用原始指针,但在大多数情况下我没有特定的所有者,但窗口是从多个地方操作的。感谢您的评论。
-
您能否详细说明这里涉及的类层次结构?还是只是那一类?还有很多其他的成员函数吗?
标签: c++ unit-testing c++11 interface smart-pointers