CIO:通过服务模拟来简化SOA开发

此SOA 开发流程的第二步是开发服务测试,以将用例系统编写为可执行格式。仅当服务恰当地实现了用例时,测试才能通过。

Kent Beck 指出,测试 应该是自动而独立的,而且应该对可能出现问题的部分进行检查(请参阅参考资料部分,以获得有关他的书籍 Extreme Programming Explained 和 Test Driven Development 的信息)。测试——通过测试开发工作软件——是 Beck 称为极限编程 (XP) 的方法所包含的十二项实践之一。它是测试驱动的开发 (TDD) 的核心——如果您只能遵循一个实践,该如何执行 XP 呢?当采用 XP 和 TDD 时,将首先开发测试,然后开发软件以通过测试,接着重复这些步骤,直到软件足够完善为止。

应该测试什么?测试的概念源于许多地方,但用例是测试的最佳来源。用例文本和关系图描述用户对需求的理解。测试以更明确的方式表述这种理解,并以可靠和重复执行的代码加以表示。用例和测试是面向不同的受众(人和计算机)以不同形式表示相同内容的对等项。

服务用例的服务测试没有什么不同,不过更多地将其看作功能测试,而不是单元测试。服务测试不会验证服务如何实现;提供程序开发团队可以自行实现此用途的单元测试。服务测试验证服务是否提供了服务用例认为其应该提供的行为。这些测试还需要对错误路径进行测试。

测试将最终定义服务的期望接口。此接口通常为 Web 服务的 Web 服务描述语言(Web Services Description Language,WSDL)文件、Java 接口或 Java 组件的 EJB 远程接口等等。如果首先开发接口,然后根据接口实现测试,可能会看起来更简单,不过更直接的方法是首先开发测试,然后开发接口,以使测试能够成功编译。

示例服务测试

可以使用简单的单元测试框架(JUnit 或 Cactus)来开发测试。该框架将充当服务的使用者,并进行使用者将进行的操作。下面是一些可能的测试:

? 使用 IBM 调用 simple quote ,以验证获得的结果是“$100.00”。

? 使用 MSFT 调用 simple quote,以验证获得的结果是“$30.00”。

? 使用 BOGUS 调用 simple quote,以验证获得的结果是 invalid stock symbol 错误。

对复杂报价和历史报价的测试将与此类似。另外,还有针对可能的基础结构错误的测试,如远程异常和 HTTP 400 错误。最后,测试应该对服务用例中指定的所有内容进行验证;如果在用例中指定了操作,但却不在一个或多个测试中进行检查,则意味着使用者不能期望提供程序将实际执行该操作。

服务模拟

此 SOA 开发流程的第三步是开发服务模拟——通过服务测试的模拟对象。这些服务模拟是实际服务提供程序的简单原型。

Kent Beck 将模拟对象 描述为测试对象,该对象可以使用常量进行响应,从而实现开销大或复杂的资源的模仿版本。例如,模拟数据库是一个简单对象,但具有数据库的 API,可以接受一些已知的 SQL 字符串,并为每个字符串返回一组固定的结果。模拟对象允许您对组件进行测试,而不必依赖于外部资源。

现在假定此外部资源是一个 SOA 服务。如果您的组件使用该服务,则测试此组件时也在测试该服务。如果服务工作不正常,或者不可用,则即使组件工作正常,测试也会失败。如果服务很慢(通过网络远程调用服务时就是这样),您的测试也会运行得很慢——这样就不能如您所愿频繁地运行测试了。而且,如果服务尚未实现,则根本就不能对您的组件进行测试。

因此,一个不错的方法就是开发服务模拟,模拟对象是实际服务的简单仿真程序。服务具有与实际服务相同的 API;它会实现针对服务测试而开发的接口。服务模拟应该如何工作?它应该通过您已经开发的服务测试,这表明模拟真的和实际服务的工作方式一样。

在某些情况下,服务模拟实际上比实际服务更适合用于进行测试工作。假定您的组件使用返回股票报价的服务。如果传入代码 IBM,您将获得什么样的结果呢?$50?$100?$150?具体取决于当前的股票价格,但这是测试的一个“鸡与蛋”问题。通过使用服务模拟,已硬编码的模拟将始终返回 $100,然后据此进行测试,与测试实际服务相比,这实际上更加可靠。

谁开发服务模拟?提供程序团队(而非协调程序团队)应该开发服务模拟。服务模拟表示提供程序团队计划实际实现的内容的简单实现。如果相同的服务有多个提供程序团队,则他们必须进行协调,以产生一个他们都认可的模拟服务。

示例服务模拟

此示例服务模拟需要通过我前面编写的示例服务测试。因此,它的简单报价实现是一个 case 语句。如果服务只是一个传统 Java 对象(plain old Java object,POJO),则对应的模拟将为通用接口的特殊实现。

如果服务更复杂(如无状态会话 Bean 或 SOAP Web 服务),此 POJO 代码仍然可以作为更复杂的模拟实现的基础。在任何情况下,模拟实现肯定都不应该试图处理每个可能的股票代码或访问具有实时数据的数据库。模拟实现应该足以通过服务测试即可。