8

我编写了一些单元测试来测试围绕 FTP 服务器 API 的包装器

单元测试和 FTP 服务器都在同一台机器上。

包装器 API 被部署到我们的平台并用于远程处理和 Web 服务场景。包装 API 本质上采用 XML 消息来执行任务,例如添加/删除/更新用户、更改密码、修改权限……诸如此类。

在单元测试中,假设将用户添加到虚拟域,我创建 XML 消息以发送到 API。API 完成它的工作并返回一个响应,其中包含有关操作是成功还是失败的状态信息(错误代码、验证失败等)。

为了验证 API 包装器代码是否确实做了正确的事情(如果响应指示成功),我调用 FTP 服务器的 COM API 并直接查询其存储以查看,例如,在创建用户帐户时,用户帐户是否真的做了被创建。

这气味难闻吗?

更新 1: @Jeremy/Nick:包装器是测试的重点,FTP 服务器及其 COM API 是第 3 方产品,可能经过良好测试且稳定。包装 API 必须解析 XML 消息,然后调用 FTP 服务器的 API。我将如何验证包装器正确设置了用户帐户的特定属性,这可能是一个愚蠢的情况。例如,由于包装代码中的拼写错误,设置了错误的 FTP 帐户的属性或属性。一个很好的例子是设置上传和下载速度限制,这些可能会在包装代码中转置。

更新2:感谢大家的回答。对于建议使用模拟的人,我已经想到了,但是那里的灯还没有打开,我仍在努力思考如何让我的包装器与 FTP 服务器的模拟一起工作. 模拟将驻留在哪里,我是否将所述模拟的实例传递给包装器 API 以使用而不是调用 COM API?我知道嘲弄但很难理解它,主要是因为我发现大多数示例和教程都非常抽象并且(我很惭愧地说)接近于难以理解。

4

8 回答 8

7

您似乎在混合单元和组件测试问题。

  • 如果您正在对包装器进行单元测试,您应该使用模拟 FTP 服务器并且不涉及实际服务器。好的一面是,您通常可以像这样实现 100% 的自动化。
  • 如果您正在对整个事物进行组件测试(包装器 + FTP 服务器一起工作),请尝试在与您的测试相同的级别上验证您的结果,即通过您的包装器 API。例如,如果您发出上传文件的命令,接下来,发出删除/下载该文件的命令,以确保文件已正确上传。对于测试结果并非易事的更复杂的操作,请考虑使用您提到的 COM API“后门”,或者可能涉及一些手动验证(您的所有测试都需要自动化吗?)。
于 2008-10-17T20:23:18.510 回答
3

为了验证 API 包装器代码是否确实做了正确的事情(如果响应指示成功),我调用了 FTP 服务器的 COM API

停在那儿。您应该模拟 FTP 服务器,并且包装器应该对模拟进行操作。

如果您的测试同时运行包装器和 FTP 服务器,则您不是单元测试。

于 2008-10-17T20:27:51.823 回答
3

要使用模拟对象测试您的包装器,您可以执行以下操作:

  • 编写一个与 FTP 服务器的 COM API 具有相同接口的 COM 对象。这将是您的模拟对象。您应该能够通过依赖注入将任何一个的接口指针传递给您的包装器来交换真实的 FTP 服务器和您的模拟对象。
  • 您的模拟对象应该基于在其接口上调用的方法(模仿 FTP 服务器 API)以及使用的参数值来实现硬编码行为:
  • 例如,如果您有一个UploadFile方法,您可以盲目地返回成功结果,并且可能将传入的文件名存储在字符串数组中。
  • 当您遇到包含“错误”的文件名时,您可以模拟上传错误。
  • 当您遇到包含“慢”的文件名时,您可以模拟延迟/超时。
  • 稍后,该DownloadFile方法可以检查内部字符串数组以查看具有该名称的文件是否已经“上传”。

一些测试用例的伪代码是:

//RealServer theRealServer;
//FtpServerIntf ftpServerIntf = theRealServer.getInterface();

// Let's test with our mock instead
MockServer myMockServer;
FtpServerIntf ftpServerIntf = myMockServer.getInterface();

FtpWrapper myWrapper(ftpServerIntf);

FtpResponse resp = myWrapper.uploadFile("Testing123");
assertEquals(FtpResponse::OK, resp);

resp = myWrapper.downloadFile("Testing123");
assertEquals(FtpResponse::OK, resp);

resp = myWrapper.downloadFile("Testing456");
assertEquals(FtpResponse::NOT_FOUND, resp);

resp = myWrapper.downloadFile("SimulateError");
assertEquals(FtpResponse::ERROR, resp);

我希望这有帮助...

于 2008-10-19T03:35:29.070 回答
2

我同意 Nick 和 Jeremy 关于不接触 API 的观点。我会考虑模拟 API。

http://en.wikipedia.org/wiki/Mock_object

如果是 .NET,您可以使用:起订量
http

://code.google.com/p/moq/ 以及一堆其他模拟库。

于 2008-10-17T20:13:33.773 回答
0

您在测试包装器或 API。API 应该按原样工作,所以我认为您不需要对其进行测试。将您的测试工作集中在包装器上,并假装 API 不存在,当我编写一个执行文件访问的类时,我不会在 streamreader 中对构建进行单元测试......我专注于我的代码。

于 2008-10-17T20:05:53.007 回答
0

我会说你的 API 在测试时应该像数据库或网络连接一样对待。不要测试它,它不在你的控制之下。

于 2008-10-17T20:08:50.880 回答
0

听起来您不是在问“我应该测试API 吗?” ——你在问“我应该使用 API 来验证我的包装器是否在做正确的事情吗?”

我说是。您的单元测试应该断言您的包装器会传递 API 报告的信息。例如,在您给出的示例中,我不知道您将如何避免接触 API。所以我不认为它闻起来很臭。

于 2008-10-17T20:20:13.383 回答
0

如果更高级别的 API 是只写的,那么我唯一能想到的可能是深入到较低级别的 API 以验证结果。例如,如果您可以使用高级 API 创建用户,那么也应该有一个高级 API 来获取用户帐户。用那个。

其他人建议模拟较低级别的 API。那很好,如果你能做到的话。如果较低级别的组件被模拟,检查模拟以确保设置正确的状态应该没问题。

于 2008-10-17T21:07:23.757 回答