Java 测试与外部服务的交互

Java 测试与外部服务的交互,java,unit-testing,testing,playframework,Java,Unit Testing,Testing,Playframework,先决条件:我使用的是最新版本的和Java版本(不是Scala) 我需要在创建用户时将消息发布到消息队列,我想测试这种行为。我的问题是使其易于测试 控制器方法 在其他框架中,我会在控制器中使用构造函数注入,并在测试中传入模拟队列;然而,与游戏!控制器是静态的,这意味着我不能在测试中执行新的MyController(mockedQueue) 我可以使用GoogleGuice并在控制器的静态字段上添加@Inject注释,但这对我来说不是很好,因为这要么意味着我必须公开字段以便在测试中替换,要么我必须在

先决条件:我使用的是最新版本的和Java版本(不是Scala)

我需要在创建用户时将消息发布到消息队列,我想测试这种行为。我的问题是使其易于测试

控制器方法 在其他框架中,我会在控制器中使用构造函数注入,并在测试中传入模拟队列;然而,与游戏!控制器是静态的,这意味着我不能在测试中执行新的MyController(mockedQueue)

我可以使用GoogleGuice并在控制器的静态字段上添加
@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()
?或者你是说你要对
排队服务
进行单元测试,但只对
用户
排队服务
之间的交互进行功能测试?如果这样做的话,没问题;但在我处理过的大多数其他框架中,测试两者之间的交互是完全可行的,而不需要实际的实时队列。那么这家工厂是从哪里来的呢?单身汉?鸡和蛋?:你可以有一个