在C ++中设置模拟接口

时间:2009-02-24 03:21:56

标签: c++ windows oop testing

我目前正在尝试使用某个SDK,它让我从供应商提供的DLL中加载函数。我必须将参数传递给这些函数,并且DLL完成所有工作..

现在,DLL应该与另一台设备通信,而我只是等待结果。但是,我没有这个设备,那么如何设置模拟界面来模拟设备?

要清楚,这是一个例子:

myfuncpointer.Open(someparam,anotherparam,...);

现在,因为我没有设备,DLL实际上无法执行上述功能;它失败。如何设置测试以便DLL与我设计的类而不是设备进行对话?有没有办法重定向DLL的调用?

如何让DummyDevice类执行此操作?

谢谢..

P.S。如果有什么不清楚的地方,请不要太快速下来投票。评论我需要解释什么,我会尽力清理它..谢谢。


编辑:但是,我所拥有的是一张规格表,其中包含所有使用的数据结构以及必须包含的预期/合法值。例如,如果我调用一个函数:

myfuncpointer.getinfo(param,otherparam);

其中一个参数是一个数据结构,DLL在查询设备后填充了信息(例如,如果启用了一个选项)..我可以这样做

param.option = true;

完成getinfo调用后。

这是测试代码的好方法吗?欺骗这个DLL来思考所有错误的东西似乎非常危险,而且似乎真的很难扩展甚至只是一点点......

6 个答案:

答案 0 :(得分:6)

仿真设备是否可以访问权宜之计解决方案,直到您获得硬件?如果是这样,我建议找一些其他方法来提高效率:处理其他事情,编写单元测试等等。

模拟设备访问是永久性要求吗?如果是这样,您可以采取以下几种方法:

  1. 如果其他供应商的SDK具有“模拟”或“模拟”模式,请使用它。您可能会感到惊讶。如果没有安装其他供应商的硬件,您可能不是唯一需要能够测试/运行其应用程序的客户端。

  2. 从图片中删除其他供应商的代码。仅模拟应用程序所需的功能,并根据程序的要求进行设置。一切都在你的掌控之中。

    一个。 添加间接层。使用多态在两个实现之间切换:一个调用另一个供应商的DLL,另一个模拟其行为。将代码调用到抽象基类而不是直接调用其他供应商的DLL也可以更容易地为代码编写单元测试。

    编写一个模拟DLL(如Adam Rosenfield建议的那样)。您需要完全匹配函数名称和调用约定。当您升级到其他供应商的DLL的新版本时,您将需要扩展模拟DLL以支持您的应用程序使用的任何新入口点。

    如果你需要选择在运行时使用哪个DLL,这可能需要转换你的代码来动态加载DLL(但听起来你可能已经这样做了,假设你说了一些关于函数指针的东西)。您可以在安装时决定是安装其他供应商的DLL还是模拟DLL。如果这纯粹是出于测试目的,您可以选择在编译时使用哪个DLL,并且只构建应用程序的两个版本。

  3. 编写模拟设备驱动程序(正如其他人所建议的那样)。

    如果您有其他供应商的用户模式DLL与其设备驱动程序之间的接口规范,这可能是可行的。即使您是经验丰富的设备驱动程序开发人员,也可能需要比其他任何方法更长的时间,特别是如果您没有其他供应商的DLL的源代码。 UMDF(用户模式驱动程序框架)可能会使这更容易或更省时。

    如果您提到的规范没有描述用户/内核接口,则需要对该接口进行反向工程。与其他选项相比,这可能不会有效地利用您的时间,特别是如果驱动程序接口很复杂(例如,它将大量复杂的数据结构传递给DeviceIoControl())。

    在这两种情况下,每次升级到其他供应商的SDK的新版本时,您都需要重新访问它。相同的导出DLL函数可能需要使用新的DeviceIoControl()代码和结构。

答案 1 :(得分:2)

这取决于DLL如何访问设备。如果通过调用另一个DLL中的函数来执行此操作,则可以提供已替换相应函数的模拟DLL。但是,如果DLL正在做一些低级别的事情,比如直接调用内核,那么你将会有更艰难的时间。在这种情况下,您可能必须编写新的设备驱动程序或内核扩展或其他内容。

答案 2 :(得分:2)

有很多地方可以插入模拟对象:

  1. 模拟对DLL的调用:在应用程序中,编写一个Facade/wrapper class,将函数指针调用封装到供应商的DLL中。这通常是个好主意,因为你可以在DLL函数调用上放置一个更干净的类API。

  2. 模拟DLL :编写自己的模拟

  3. 的DLL
  4. 模拟设备:编写模拟设备的设备驱动程序。我不推荐这个,因为它会做很多工作,并且很难模仿设备的行为(包括错误和怪癖)。

  5. 我建议#1 ,因为它是最少量的工作而且不是丢弃代码。

    您设计用于隐藏对供应商DLL的调用的Facade类接口将改进其他代码的设计。它将减少代码与供应商的DLL和API的低级实现细节的耦合。如果供应商提供新的DLL API,您将能够更轻松地将其与代码集成。您甚至可以用不同的供应商或设备替换该供应商的DLL。 :)

答案 3 :(得分:1)

您可能必须为此编写设备驱动程序。根据您的平台,您可能会有可用的设备(例如Linux中的“lo”界面)。

你不是第一个遇到这个问题的人,所以在推出自己的网站之前一定要先搜索网页: - )

答案 4 :(得分:1)

如果DLL正在与外部硬件设备通信,则需要实现与实际驱动程序兼容但具有模拟响应的驱动程序。

最有可能的是,如果您没有该设备,那么无论如何SDK都将无用。如果提供的某些功能与此外部设备无关,那么在没有硬件依赖性的情况下获取等效库几乎肯定会更容易。

答案 5 :(得分:-1)

我在WIN32环境中使用了一种相对hacky,非可移植的方法来模拟函数(我认为它只有32位,但不确定)。这是:

#define INJECTED_BYTES  5

static void replace_target(void *Target, void *Server)
{
    DWORD dwOld;

    VirtualProtect(Target, INJECTED_BYTES, PAGE_WRITECOPY, &dwOld);
    *((unsigned char *)Target)++ = 0xe9 ;   // jump relative
    *(unsigned int *)Target = (unsigned int)((unsigned char *)Server - (unsigned char *)Target)-4;
    VirtualProtect(((unsigned char *)Target)-1, INJECTED_BYTES, PAGE_EXECUTE, &dwOld);
    FlushInstructionCache(GetCurrentProcess(), 0, 0);
}

这样做,它通过跳转到模拟函数来替换目标函数的前几个字节。因此,当您调用目标时,它会直接跳转到您的存根。只需记住保留替换字节的副本(INJECTED_BYTES),以便稍后撤消模拟。

想一想,是什么阻止你编写自己的DLL,只能找出真正的DLL所做的?