Проверка контроллера не удалась при попытке отправить объект POST с аннотациями @JsonProperty к конечной точке, аннотированной @Valid на @RequestBody - PullRequest
0 голосов
/ 21 декабря 2018

У меня проблема с тестированием конечной точки контроллера покоя.Я пытаюсь отправить объект POST в конечную точку, которая помечена @Valid в поле @RequestBody.Поля сущностей снабжены правилами валидации и аннотациями Джексона.

Я пытаюсь проверить конечную точку 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());
    }
}

Что важно.Когда объект извлекается (GET) с помощью swagger, поле «userPassword» не сериализуется.Он работает, как и ожидалось, благодаря аннотации @JsonProperty.Когда объект POSTed с swagger также работает правильно, пароль сохраняется в базе данных, а ответ не содержит поля «userPassword».Кроме того, если какое-либо обязательное поле (помеченное @NotEmpty) отсутствует, генерируется исключение.

Тесты, которые проверяют выборку данных, работают правильно, 'userPassword' не является видимым ответом в json

andExpect(jsonPath("$.userPassword").doesNotExist())

Но когда присоединенный тест выполняется, он дает сбой с неправильным состоянием (400 вместо 200) и информация "Пароль пользователя должен быть заполнен."Пароль заполнен, но похоже, что он не передан в тело запроса.Я сделал некоторую отладку и заметил, что когда @JsonProperty добавляется в поле «userPassword», это поле устанавливается в «null».

// edit
jsonUser, используемый для публикации новых данных пользователя в формате 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();
    }
}

// edit
Я нашел решение для моей проблемы. jsonUser.write , чтобы работать, сначала нужно прочитать данные из newUser (так очевидно) и включить аннотацию json.Поскольку getter заблокирован jsonproperty, он пропускается во время чтения.Вот почему тестовый пример не работает, но 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());

1 Ответ

0 голосов
/ 21 декабря 2018

Вы устанавливаете содержимое сообщения с помощью

jsonUser.write(newUser).getJson()

jsonUser является 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-запроса.Когда Джексон приходит десериализовать ваш json из содержимого запроса POST, он не найдет пароль.Вот почему вы видите то, что видите.

Я понимаю, почему вы используете опцию WRITE_ONLY в своем классе, имеет смысл предотвратить запись пароля при сериализации.Однако это означает, что вы не можете использовать этот класс для создания json, который вы хотите отправить на сервер, просто сериализовав его.

Один из подходов состоит в том, чтобы создать подкласс вашего пользователя с помощью TestUser, который только что имелполе пароля.Затем используйте TestUser в вашем JacksonTester.

public class TestUser extends User {
    private String userPassword;
}

Затем в своем тестовом классе вы замените пользователя на TestUser

private JacksonTester<TestUser> jsonUser;

и

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