先决条件:我使用的是最新版本的Play! framework和Java版本(不是Scala)。
我需要在创建用户时将消息发布到消息队列,并且我想测试该行为。我的问题是让这个很容易测试。
在其他框架中,我要做的就是在控制器中使用构造函数注入,并在我的测试中传入一个模拟队列;然而,玩Play!控制器是静态的,这意味着我在测试中无法new MyController(mockedQueue)
。
我可以使用Google Guice并在我的控制器中的静态字段上添加@Inject
注释,但这对我来说并不是很好,因为它或者意味着我必须公开该字段才能被替换在测试中,或者我必须在我的测试中使用容器。我更喜欢使用构造函数注入,但玩!似乎不方便。
通常说你的逻辑应该在你的模型中,而不是你的控制器。那讲得通;但是,我们在这里不是Ruby,让你的实体与外部服务(电子邮件,消息队列等等)进行交互的可测性远远低于动态环境,在这种环境中你可以用你的MessageQueue
静态调用替换随意的模拟实例。
如果我让我的实体呼叫到队列,那该怎么可测试?
当然,如果我进行端到端的集成测试,这两种情况都是不必要的,但我不需要为我的测试运行而需要一个消息队列或SMTP服务器。
所以我的问题是:我如何为我的游戏建模!控制器和/或模型,以促进测试与外部服务的交互?
答案 0 :(得分:3)
正如我所看到的,对此没有一个干净的解决方案。
您可以使用Abstract Factory作为依赖项。这个工厂可以为它生成的对象设置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 PowerMock来模拟静态方法。我之前使用过它,对大多数情况来说效果相对较好。只是不要过度使用它,或者你在维护地狱......
最后,由于您愿意在您的应用程序中使用Guice,this可能是一个可行的选择。
祝你好运!答案 1 :(得分:2)
我有点困惑。你可以调用另一个类的方法
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);
}
}
您可以使用模拟为 QueueService 编写单元测试用例,并为用户控制器保存方法编写功能测试用例。
答案 2 :(得分:2)
编辑:扩展答案,因为以前不清楚
第一个想法是将对Queue的引用添加到Model中,因为您有POJO并且可以访问构造函数。正如你在下面的评论中提到的那样,在考虑Hibernate为实体提供保护时,模型方法是有问题的,这会丢弃它。
第二种方法是将对Queue的引用添加到Controller。现在,这似乎是一个坏主意。除了你提到的公共成员问题,我相信控制器背后的想法是检索请求的参数,验证它们是否正确(checkAuthenticity,验证等),发送要处理的请求然后准备响应。
这里的“密钥”是“发送要处理的请求”。在某些情况下,如果它很简单,我们可以在Controller中完成这项工作,但在其他情况下,最好使用“服务”(以某种方式调用它),在该服务中,您可以使用给定数据完成所需的工作。
我使用这种分离,从测试的角度来看,对我来说,通过Selenium测试控制器更容易,并为服务进行单独的测试(使用JUnit)。
在这种情况下,此服务将包含对您提及的队列的引用。
关于如何初始化,这将取决于。你可以创建一个单例,每次通过构造函数初始化它等等。在你的特定场景中,这可能取决于与初始化队列服务相关的工作:如果很难你可能想要一个带有Factory方法的Singleton来检索服务(并且可以在测试中进行模拟)并将其作为参数传递给Service对象的构造函数。
希望这个更新能够澄清我回答时的想法。
答案 3 :(得分:1)
这可能不是你想要的,但在我目前的项目中,我们通过集成测试和带有本地队列和消息传递桥的JMS设置解决了这种类型的测试。
稍微详细一点:
在我的项目中,我们使用SoapUI来执行这些测试,因为被测系统是基于SOAP的集成平台,SoapUI具有良好的JMS支持。但它也可以是一个简单的JUnit测试,它会执行测试并在之后从本地JMS队列中读取。