编写 "clean" 集成测试 - 使用 jersey 2 和 Spring 的 REST

Writing "clean" integration tests - REST using jersey 2 and Spring

我希望下面写的测试用例是一个"clean"测试,即测试后自动回滚数据库更改。

@Test
public void testUpdate(){
    ClientConfig clientConfig = new ClientConfig();
    clientConfig.register(JacksonFeature.class);
    Client client = ClientBuilder.newClient(clientConfig);
    Name name = new Name(1L,"Updated","Updated");
    Response response = client
            .target("http://localhost:8080/jersey-spring/name/").request().header("Content-type", MediaType.APPLICATION_JSON).post(Entity.json(name));
    Assert.assertTrue(response.getStatus() == 200);
}

其余服务在集成测试期间使用jetty-maven-plugin部署。我现在面临的问题是我无法使用 spring test transactional 测试,这意味着数据库的任何状态更改都需要在测试后手动清理,这非常麻烦。

所以我的问题是,当使用 jersey 作为 REST 提供程序时,运行 "clean" 集成测试的建议方法是什么。我也尝试过使用 JerseyTest,但也遇到了同样的问题。

出于各种原因,我无法用 Spring MVC 替换 Jersey。

更新

  1. 不涉及嘲讽。测试是端到端的。
  2. 我喜欢接近集成测试的生产配置,因此没有内存数据库用于集成测试。

我在许多 Spring+Jersey2 项目中使用了以下配置,并且工作正常。

  • 使用 JerseyTest 并在您的测试中扩展它 class
  • 使用 or 在 REST 控制器中模拟您的服务
  • 这很重要。使用内存数据库(我更喜欢 HSQLDB)并在内存数据库中创建一个针对此的测试应用程序上下文。因此您不必担心数据完整性等问题
  • 通过覆盖 JerseyTestconfigure() 方法配置您的测试 class,注入您的模拟并将 REST 控制器的任何相关服务绑定到 Jersey Context。

这是一个例子:

@Override
protected Application configure() {
    // Bind factories of classes to be mocked.
    AbstractBinder binder = new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(MockCrawlerManagerFactory.class).to(CrawlerManager.class);
            bindFactory(MockUrlQueueServiceFactory.class).to(UrlQueueService.class);
            bindFactory(MockSitemapServiceFactory.class).to(SitemapService.class);
            bindFactory(MockCrawlerServiceFactory.class).to(CrawlerService.class);
            bindFactory(MockUrlContentServiceFactory.class).to(UrlContentService.class);
            // Other services to mock ....
        }
    };

    // This is your main Spring application
    ApplicationContext context = new AnnotationConfigApplicationContext(Bootstrap.class);

    // And create your Jersey context here, register REST controller to Jersey context
    return new ResourceConfig().registerClasses(RestController.class, JacksonJsonProvider.class, JacksonJaxbJsonProvider.class)
            .packages("com.company.foo.crawler.rest", "com.company.foo.crawler.exception").register(binder).property("contextConfig", context);
}
  • 并开始编写测试:)

示例测试:

    @Test
    public void testCreateCrawlerWithNoConfiguration() throws ExecutionException, InterruptedException {
    String mockUrl = "/version/" + VERSION + "/crawlers";
    Map<String, Object> data = new HashMap<>();
    data.put("configurationId", "null");
    data.put("startUrl", "http://blog.ahmetbutun.net");
    Future<Response> asyncRes = target(mockUrl).request(MediaType.APPLICATION_JSON_TYPE).header("userId", MOCK_CRAWLER_VALID_USER_ID).header("token", AUTHENTICATION_HEADER)
            .async().post(Entity.json(data));

    RestResponse response = asyncRes.get().readEntity(RestResponse.class);

    Assert.assertNotNull(response);
    Assert.assertEquals(AppConstants.ERROR_CODE_INVALID_CONFIGURATION_ID_EXCEPTION, response.getStatusCode());
    Assert.assertEquals(HttpStatus.BAD_REQUEST.value(), response.getMessage().getStatus());
}

希望这对您有所帮助。

只给我 2 美分。我在我的堆栈中使用了一些特定的东西,所以它可能只是我,可能不适合你的堆栈。值得一提的是none少

我假设您将使用一些 ORM 框架来执行数据库操作,当我在我的测试中创建会话时,我将我的休眠指向内存中的 H2(我的测试点上的 hbm 配置指向 H2)。

我使用 liquibase 进行数据库迁移,在测试中我将它们指向这些 H2 数据库(您可以在测试设置中执行此操作)。我的 DAO 然后使用这个数据库进行操作,在这里我可以测试我的数据库操作并编写断言。

您不能让您的 REST 处理程序方法参与包装您的测试代码的事务,这是不可能的。但是,您可以做的是在测试后 and/or 之前使用 DBUnit 使数据库进入干净状态。

我特别推荐使用 Spring Test DBUnit library,因为它与 Spring 进行了出色的注释驱动集成。

此外,我建议使用 Docker 来配置您的数据库,因为依赖本地数据库并不是一个好的做法,因为您的构建过程将具有外部依赖性,并且需要在 CI 服务器和每台开发机器。我以前用过 Overcast, here's a fine example of how to use it with Docker.