尝试将带有 @JsonProperty 注释的实体 POST 连接到 @RequestBody 上带有 @Valid 注释的端点时,控制器测试失败

Controller test failed while try to POST entity with @JsonProperty annotations to endpoint annotated with @Valid on @RequestBody

我在测试其余控制器端点时遇到问题。 我正在尝试 POST 实体到在 @RequestBody 字段上用 @Valid 注释的端点。实体字段使用验证规则和 jackson 注释进行注释。

我正在尝试使用单元测试来测试 POST 端点。

带注释的实体和字段

public class User {
    //other fields 

    @Column(name = "PASSWORD", nullable = false)
    @NotEmpty(message = "Users password must be filled.")
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String userPassword;
}

控制器

@RestController
@RequestMapping(value = "/api/users", produces = "application/hal+json")
public class UserController {

    @PostMapping("")
    public ResponseEntity<User> addNewUser(@Valid @RequestBody User newUser) {
        User createdUser = userService.saveUserInDatabase(newUser);
        return new ResponseEntity<>(createdUser, HttpStatus.OK);
    }
}

和单元测试

@RunWith(MockitoJUnitRunner.class)
public class UserControllerTest {
    //other tests 
    @Test
    public void shouldBeAbleToAddNewUser() throws Exception {
        User newUser = new User();
        newUser.setUserName("userName");
        newUser.setUserEmail("userName@domain.com");
        newUser.setUserPassword("secret1");
        newUser.setEnable(true);

        User createdUser = new User(1L, "userName", "userName@domain.com", "secret1", true);

        when(userService.saveUserInDatabase(any(User.class))).thenReturn(createdUser);

        mockMvc.perform(post("/api/users")
                    .contentType(MediaTypes.HAL_JSON_UTF8)
                    .content(jsonUser.write(newUser).getJson()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.userName", Matchers.equalToIgnoringCase(createdUser.getUserName())))
                .andExpect(jsonPath("$.userId", Matchers.is(createdUser.getUserId().intValue())))
                .andExpect(jsonPath("$.userPassword").doesNotExist());
    }
}

重要的是什么。当使用 swagger 获取 (GET) 实体时,字段 "userPassword" 未序列化。由于 @JsonProperty 注释,它按预期工作。当使用 swagger POSTed 实体也能正常工作时,密码保存在数据库中并且响应不包含 "userPassword" 字段。此外,如果缺少任何必需的(用@NotEmpty 注释的)字段,则会抛出异常。

正在测试获取数据的测试,工作正常,'userPassword' 在 json

中不可见响应
andExpect(jsonPath("$.userPassword").doesNotExist())

但是当执行附加测试时,它失败并显示错误的状态(400 而不是 200)和信息 "Users password must be filled." 密码已填写,但看起来没有推送到请求正文。我做了一些调试并注意到当@JsonProperty 添加到 "userPassword" 字段时,该字段设置为 "null".

//编辑
json用户习惯于 post 新的 JSON 格式的用户数据是这样定义的

@RunWith(MockitoJUnitRunner.class)
public class UserControllerTest {

//other variables and methods

private JacksonTester<User> jsonUser;

    @Before
    public void setup() {
        JacksonTester.initFields(this, new ObjectMapper());
        mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
    }
}

//编辑
我找到了解决我的问题的方法。 jsonUser.write 工作必须首先从 newUser 读取数据(很明显)并包含 json 注释。由于 getter 被 json属性 阻塞,因此在读取期间被省略。这就是为什么测试用例不起作用但 POST 通过 swagger 正常工作的原因。 为了解决这个问题,我准备了包含 json 格式

数据的字符串
String userDetails = "{\"userName\":\"userName\",\"userEmail\":\"userName@domain.com\",\"userPassword\":\"secret1\",\"enable\":true}";

mockMvc.perform(post("/api/users")
                    .contentType(MediaTypes.HAL_JSON_UTF8)
                    .content(userDetails)) 
                    .andExpect(status().isOk());

你用

设置了post的内容
jsonUser.write(newUser).getJson()

json用户是 JacksonTester 并被赋予 Jackson ObjectMapper 以执行序列化/反序列化。当您使用 write 方法序列化您的用户对象时,它将遵守

@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)

混淆可能是 'write' 的使用。 JacksonTester 上的方法是 write() 这意味着序列化(将对象写入 json),而 JsonProperty.Access.WRITE_ONLY 意味着仅在反序列化期间使用此字段(从 json 写入对象)

而您获得的 Json 将不包含任何密码详细信息。即你得到类似的东西:

{"userName":"userName","userEmail":"userName@domain.com","enable":true}

您将其用作测试 POST 请求的内容。当 Jackson 从 POST 请求的内容反序列化您的 json 时,它不会找到密码。这就是为什么你看到你所看到的。

我明白你为什么要在 class 上使用 WRITE_ONLY 选项,防止密码在序列化时被写出是有意义的。但是,这意味着您不能使用 class 通过简单地序列化它来创建要发送到服务器的 json。

一种方法是用一个只有密码字段的 TestUser 子class 您的用户。然后在你的 JacksonTester 中使用 TestUser。

public class TestUser extends User {
    private String userPassword;
}

然后在您的测试中 class 您将 User 替换为 TestUser

private JacksonTester<TestUser> jsonUser;

    TestUser newUser = new TestUser();
    newUser.setUserName("userName");
    newUser.setUserEmail("userName@domain.com");
    newUser.setUserPassword("secret1");
    newUser.setEnable(true);