jersey 参数验证_在Jersey 2中进行集成测试自定义验证约束

jersey 参数验证

我最近加入了一个团队,试图将一个完整的遗留系统转换为Java中的RESTful服务集。 他们决定将最新的2.x版本的Jersey用作REST容器,这对我来说不是首选,因为我不是JSR- *规范的忠实拥护者。 但是现在我必须承认,JAX-RS 2.x的运行正确:需要几乎零的样板代码,支持功能的自动发现,并且像其他现代框架一样,更喜欢使用约定而不是配置。 由于该规范还很年轻,因此很难找到具有一些可用代码的优秀教程和启动项目。 我在GitHub上创建了jersey2-starter项目,该项目可用作您自己的生产就绪的RESTful服务的起点。 在本文中,我将介绍如何实现和集成测试您自己的REST资源验证约束。

自定义约束

用Java编写REST时困扰我的问题之一是用注释乱扔您的类模型。 假设您要构建一个简单的Todo列表REST服务,当使用Jackson,validation和Spring Data时,您可以轻松地将其作为实体类结束:

@Document
public class Todo {
    private Long id;
    @NotNull
    private String description;
    @NotNull
    private Boolean completed;
    @NotNull
    private DateTime dueDate;

    @JsonCreator
    public Todo(@JsonProperty("description") String description, @JsonProperty("dueDate") DateTime dueDate) {
        this.description = description;
        this.dueDate = dueDate;
        this.completed = false;
    }
    // getters and setters
}

您的域模型现在几乎在所有地方都被混乱的注释有效地模糊了。 让我们看看如何使用验证约束( @NotNull )。 有人可能会说您可以引入一些具有自己的验证规则的DTO层,但是这与纯REST API设计对我来说是冲突的,后者表示您在应映射到您的域类的资源上进行操作。 另一方面– Todo对象有效是什么意思? 创建Todo ,应提供说明和截止日期,但是更新时会怎样? 您应该能够更改描述,到期日(推迟)和完成标志(标记为完成)中的任何一个,但是您至少应提供其中之一作为有效的修改。 因此,我的想法是引入自定义验证约束,这些约束用于创建和修改:

@Target({TYPE, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = ValidForCreation.Validator.class)
public @interface ValidForCreation {
    //...
    class Validator implements ConstraintValidator<ValidForCreation, Todo> {
    /...
        @Override
        public boolean isValid(Todo todo, ConstraintValidatorContext constraintValidatorContext) {
            return todo != null
                && todo.getId() == null
                && todo.getDescription() != null
                && todo.getDueDate() != null;
        }
    }
}

@Target({TYPE, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = ValidForModification.Validator.class)
public @interface ValidForModification {
    //...
    class Validator implements ConstraintValidator<ValidForModification, Todo> {
    /...
        @Override
        public boolean isValid(Todo todo, ConstraintValidatorContext constraintValidatorContext) {
            return todo != null
                && todo.getId() == null
                && (todo.getDescription() != null || todo.getDueDate() != null || todo.isCompleted() != null);
        }
    }
}

现在,您可以将验证注释移至REST端点的定义:

@POST
@Consumes(APPLICATION_JSON)
public Response create(@ValidForCreation Todo todo) {...}

@PUT
@Consumes(APPLICATION_JSON)
public Response update(@ValidForModification Todo todo) {...}

现在,您可以从模型中删除那些NotNull

整合测试

集成测试通常有两种方法:

  • 测试是在与应用程序不同的JVM上运行的,而该应用程序则部署在其他集成环境中
  • 测试在设置块中以编程方式部署应用程序。

两者都有其优点和缺点,但是对于足够小的服务,我个人更喜欢第二种方法。 设置起来非常容易,并且您只启动了一个JVM,这使得调试非常容易。 您可以使用Arquillian之类的通用框架在容器环境中启动应用程序,但是我更喜欢简单的解决方案,而只是使用嵌入式Jetty。 为了使测试设置与100%的生产相当,我正在创建完整的Jetty的WebAppContext并且必须解决所有运行时相关性,才能使Jersey自动发现正常工作。 这可以通过Arrinklian子项目Shrinkwrap解决的Maven 轻松实现:

WebAppContext webAppContext = new WebAppContext();
    webAppContext.setResourceBase("src/main/webapp");
    webAppContext.setContextPath("/");
    File[] mavenLibs = Maven.resolver().loadPomFromFile("pom.xml")
                .importCompileAndRuntimeDependencies()
                .resolve().withTransitivity().asFile();
    for (File file: mavenLibs) {
        webAppContext.getMetaData().addWebInfJar(new FileResource(file.toURI()));
    }
    webAppContext.getMetaData().addContainerResource(new FileResource(new File("./target/classes").toURI()));

    webAppContext.setConfigurations(new Configuration[] {
        new AnnotationConfiguration(),
        new WebXmlConfiguration(),
        new WebInfConfiguration()
    });
    server.setHandler(webAppContext);

此Stackoverflow线程在这里启发了我很多)

现在是该文章最后一部分的时候了:参数化我们的集成测试。 由于我们要测试验证约束,因此有许多要检查的边缘路径(并使您的代码覆盖率接近100%)。 每个案例编写一个测试可能是一个坏主意。 在实用的JUnit解决方案中,我最相信实用主义者团队的Junit Params 。 它非常简单,并且具有用于创建提供程序的类似于JQuery的帮助程序的概念。 这是我的测试代码(我也在这里使用构建器模式来创建各种Todos):

@Test
@Parameters(method = "provideInvalidTodosForCreation")
public void shouldRejectInvalidTodoWhenCreate(Todo todo) {
    Response response = createTarget().request().post(Entity.json(todo));

    assertThat(response.getStatus()).isEqualTo(BAD_REQUEST.getStatusCode());
}

private static Object[] provideInvalidTodosForCreation() {
    return $(
        new TodoBuilder().withDescription("test").build(),
        new TodoBuilder().withDueDate(DateTime.now()).build(),
        new TodoBuilder().withId(123L).build(),
        new TodoBuilder().build()
    );
}

OK,足够的阅读知识,随时可以克隆项目并开始编写REST服务!

参考: 我们的JCG合作伙伴 Piotr Jagielski的“ 全栈JVM开发”博客中, 在Jersey 2中集成测试自定义验证约束

翻译自: https://www.javacodegeeks.com/2014/03/integration-testing-custom-validation-constraints-in-jersey-2.html

jersey 参数验证

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值