Java 测试与外部服务的交互
先决条件:我使用的是最新版本的和Java版本(不是Scala) 我需要在创建用户时将消息发布到消息队列,我想测试这种行为。我的问题是使其易于测试 控制器方法 在其他框架中,我会在控制器中使用构造函数注入,并在测试中传入模拟队列;然而,与游戏!控制器是静态的,这意味着我不能在测试中执行新的MyController(mockedQueue) 我可以使用GoogleGuice并在控制器的静态字段上添加Java 测试与外部服务的交互,java,unit-testing,testing,playframework,Java,Unit Testing,Testing,Playframework,先决条件:我使用的是最新版本的和Java版本(不是Scala) 我需要在创建用户时将消息发布到消息队列,我想测试这种行为。我的问题是使其易于测试 控制器方法 在其他框架中,我会在控制器中使用构造函数注入,并在测试中传入模拟队列;然而,与游戏!控制器是静态的,这意味着我不能在测试中执行新的MyController(mockedQueue) 我可以使用GoogleGuice并在控制器的静态字段上添加@Inject注释,但这对我来说不是很好,因为这要么意味着我必须公开字段以便在测试中替换,要么我必须在
@Inject
注释,但这对我来说不是很好,因为这要么意味着我必须公开字段以便在测试中替换,要么我必须在测试中使用容器。我更喜欢使用构造函数注入,但是玩吧!这似乎不利于这一点
模型方法
人们常说,逻辑应该在模型中,而不是控制器中。这是有道理的;然而,我们不在Ruby中,让您的实体与外部服务(电子邮件、消息队列等)交互的可测试性要比在动态环境中的可测试性低得多,在动态环境中,您可以随意用模拟实例替换MessageQueue
静态调用
如果我将我的实体调用到队列中,这是如何可测试的
当然,如果我进行端到端集成测试,这两种情况都是不必要的,但我不希望为了运行测试而需要消息队列或SMTP服务器
所以我的问题是:我该如何模拟我的游戏!有助于测试与外部服务交互的控制器和/或模型?我有点困惑。您可以调用另一个类的方法
public class Users extends Controller {
public static void save(@Valid User user) {
//check for user validaton
user = user.save();
QueueService queueService = new QueueSerice();
queueService.publishMessage(user);
}
}
您可以使用模拟方法为队列服务编写单元测试用例,并为用户编写功能测试用例。控制器保存方法。编辑:如前所述扩展答案不清楚 第一个想法是将对队列的引用添加到模型中,因为您拥有POJO和对构造函数的访问权。正如您在下面的评论中所提到的,当考虑Hibernate对实体进行水合时,模型方法是有问题的,这将丢弃此方法 第二种方法是将此引用添加到控制器的队列中。现在,这似乎是个坏主意。除了您提到的公共成员问题之外,我相信控制器背后的想法是检索请求的参数,验证它们是否正确(检查真实性、验证等),发送要处理的请求,然后准备响应 这里的“键”是“发送要处理的请求”。在某些情况下,如果简单的话,我们可以在控制器中完成这项工作,但在其他情况下,似乎最好使用“服务”(以某种方式称之为“服务”),在该服务中,您可以使用给定的数据完成所需的工作 从测试的角度来看,我使用这种分离更容易(对我来说)通过Selenium测试控制器,并对服务进行单独的测试(使用JUnit) 在您的情况下,此服务将包括对您提到的队列的引用 这取决于如何初始化。您可以创建一个单例,每次通过构造函数初始化它,等等。在您特定的场景中,这可能取决于初始化队列服务的相关工作:如果很难,您可能需要一个带有工厂方法的单例来检索服务(并且可以在测试中模拟)并将其作为参数传递给服务对象的构造函数
希望这次更新能澄清我在回答问题时的想法。这可能不是您想要的,但在我当前的项目中,我们通过集成测试和带有本地队列和消息传递桥的JMS设置解决了这类测试 更详细地说:
- 您的代码始终向本地队列(即本地应用程序服务器上的队列,而非外部系统上的队列)发送/读取消息
- 当需要时(例如在生产环境或手动测试环境中),消息传递网桥将本地队列连接到外部服务的队列
- 集成测试创建新用户(或任何您想要测试的用户),然后从本地队列读取预期的消息。在这种情况下,消息传递网桥不处于活动状态
在我的项目中,我们使用来执行这些测试,因为被测试的系统是基于SOAP的集成平台,并且SoapUI具有良好的JMS支持。但是它也可以是一个普通的JUnit测试,它执行测试并在测试之后从本地JMS队列读取数据。正如我所看到的,没有一个干净的解决方案 您可以对依赖项使用。该工厂可以为其生产的对象提供setter方法
public class MyController {
...
private static ServiceFactory serviceFactory = ServiceFactory.getInstance();
...
public static void action() {
...
QueueService queue = serviceFactory.getQueueService();
...
}
}
您的测试如下所示:
public void testAction() {
QueueService mock = ...
...
ServiceFactory serviceFactory = ServiceFactory.getInstance();
serviceFactory.setQueueService(mock);
...
MyController.action();
verify(mock);
}
如果不想公开工厂的setter方法,可以在测试中创建接口并配置实现类
另一个选择是使用o模拟静态方法。我以前用过,在大多数情况下效果相对较好。只是不要过度使用它,否则你会在维护地狱
最后,由于您愿意在应用程序中使用Guice,这可能是一个可行的选择
祝你好运 也许我不清楚。在测试中,您将如何用模拟实例替换
new QueueService()
?或者你是说你要对排队服务
进行单元测试,但只对用户
和排队服务
之间的交互进行功能测试?如果这样做的话,没问题;但在我处理过的大多数其他框架中,测试两者之间的交互是完全可行的,而不需要实际的实时队列。那么这家工厂是从哪里来的呢?单身汉?鸡和蛋?:你可以有一个