编写 "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。
更新
- 不涉及嘲讽。测试是端到端的。
- 我喜欢接近集成测试的生产配置,因此没有内存数据库用于集成测试。
我在许多 Spring+Jersey2 项目中使用了以下配置,并且工作正常。
- 使用
JerseyTest
并在您的测试中扩展它 class
- 使用 mockito or powermock 在 REST 控制器中模拟您的服务
- 这很重要。使用内存数据库(我更喜欢 HSQLDB)并在内存数据库中创建一个针对此的测试应用程序上下文。因此您不必担心数据完整性等问题
- 通过覆盖
JerseyTest
的 configure()
方法配置您的测试 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.
我希望下面写的测试用例是一个"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。
更新
- 不涉及嘲讽。测试是端到端的。
- 我喜欢接近集成测试的生产配置,因此没有内存数据库用于集成测试。
我在许多 Spring+Jersey2 项目中使用了以下配置,并且工作正常。
- 使用
JerseyTest
并在您的测试中扩展它 class - 使用 mockito or powermock 在 REST 控制器中模拟您的服务
- 这很重要。使用内存数据库(我更喜欢 HSQLDB)并在内存数据库中创建一个针对此的测试应用程序上下文。因此您不必担心数据完整性等问题
- 通过覆盖
JerseyTest
的configure()
方法配置您的测试 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.