我们如何对与第三方COM对象交互并实例化的代码进行单元测试?

时间:2020-03-05 18:55:25  来源:igfitidea点击:

目前阻碍我全力投入单元测试的最大问题之一是,我编写的代码中很大一部分很大程度上依赖于来自不同来源的第三方COM对象,这些对象也往往会相互交互(请使用几个帮助程序库为Microsoft Office编写加载项)。

我知道我应该使用模拟对象,但是在这种情况下我将如何处理呢?我可以看到,当我只需要传递对已经存在的对象的引用时,这是相对容易的,但是我的一些例程会实例化外部COM对象本身,然后有时将它们从另一个库传递给其他外部COM对象。

最佳做法是什么?我是否应该让我的测试代码临时更改注册表中的COM注册信息,以便被测试的代码将实例化我的模拟对象之一?我应该注入修改后的类型库单元吗?还有什么其他方法?

我将特别感谢Delphi的示例或者工具,但同样对更一般的建议和更高级的解释也感到满意。

谢谢,

奥立佛

解决方案

回答

归结为"为可测试性设计"。理想情况下,我们不应直接实例化这些COM对象,而应通过可以被模拟对象替代的间接层访问它们。

现在,COM本身确实提供了一定程度的间接性,我们可以提供一个模拟对象,该对象可以替代真正的对象,但是我怀疑创建它会很麻烦,并且我怀疑我们是否会从现有的模拟框架中获得很多帮助。

回答

我会在第三方COM对象周围编写一个瘦包装器类,该类可以在单元测试情况下加载模拟对象而不是实际的COM对象。我通常通过让第二个构造函数(我称为传递模拟对象)来完成此操作。普通的构造函数只需按正常方式加载COM对象即可。

维基百科文章对该主题进行了很好的介绍
维基百科

回答

传统方法说,客户端代码应使用包装器,该包装器负责实例化COM对象。然后可以轻松地模拟该包装器。

因为代码中有直接直接实例化COM对象的部分,所以这确实不合适。如果可以更改该代码,则可以使用工厂模式:他们使用工厂创建COM对象。我们可以模拟工厂以返回替代对象。

是否通过包装器或者通过原始COM接口访问对象取决于我们。如果选择模拟COM接口,请记住在模拟中插入IUnknown :: QueryInterface,这样就知道我们已经模拟了所有接口,尤其是当对象随后传递给其他COM对象时。

或者,签出CoTreateAsClass方法。我从未使用过它,但是它可能会满足需求。