Как передать сеттер в другой метод в виде параметра?
Мне нужно переписать тест так, чтобы сеттер передавался в виде параметра в другой метод. Т.е. внутри теста создаю валидную книгу, беру из параметра не валидное значение, беру сеттер как функцию, и эта функция невалидное значение сетит в нужное поле книги.
Код теста:
@ParameterizedTest
@MethodSource("provideInvalidFields")
void saveBookShouldThrowsExceptionIfInvalidArguments(
String field, String value, String errorMessage) throws Exception {
var book = new Book();
setField(book, field, value);
var bookJson = objectMapper.writeValueAsString(book);
mockMvc
.perform(post("/api/v1/books").contentType(MediaType.APPLICATION_JSON).content(bookJson))
.andExpect(status().isBadRequest())
.andExpect(
jsonPath("$.errorMessage")
.value("Failed to create a new book, the request contains invalid fields"))
.andExpect(jsonPath("$.errors[0].field").value(field))
.andExpect(jsonPath("$.errors[0].message").value(errorMessage));
verify(libraryService, never()).addNewBook(book);
}
Код метода setField():
private static void setField(Book book, String field, String value) {
switch (field) {
case "name" -> {
book.setName(value);
book.setAuthor("Valid Author");
}
case "author" -> {
book.setName("Valid Name");
book.setAuthor(value);
}
}
}
Метод, который передает параметры, выглядит так (вставил небольшой кусок, потому что Stream довольно большой):
private static Stream<Arguments> provideInvalidFields() {
return Stream.of(
Arguments.of(
"name", "x", "Book name must be longer than 5 characters, shorter than 100 characters"),
// ...
);
}
Ответы (1 шт):
В данном случае сеттер можно представить как BiConsumer<Book, T>
, где T
-- тип поля, за которое отвечает данный сеттер.
То есть, метод setField
следует переписать, чтобы он принимал ссылку на метод для типа Book
Book::setFoo
в качестве первого параметра, а также некий экземпляр Book
и невалидное значение для сеттера.
В обобщённом виде он может выглядеть так:
private static <T> setField(BiConsumer<Book, T> setter, Book book, T value) {
setter.accept(book, value);
}
Соответственно, понадобится отдельный метод, чтобы создавать валидный экземпляр Book
, для которого будет вызываться сеттер с "плохим" значением:
// создать валидный экземпляр со всеми валидными полями
private static Book newValidBook() {
return new Book("Valid Name", "Valid Author", "Valid Whatever"); }
@ParameterizedTest
@MethodSource("provideInvalidFields")
void saveBookShouldThrowsExceptionIfInvalidArguments(
BiConsumer<Book, String> setter, String badValue, String errorMessage) throws Exception {
var book = newValidBook();
// и "испортить" поле
setField(setter, book, badValue);
var bookJson = objectMapper.writeValueAsString(book);
// ...
// основной код теста
}
Соответственно, в методе-провайдере тестовых данных вместо названия поля следует возвращать нужный сеттер / невалидное значение / сообщение об ошибке:
private static Stream<Arguments> provideInvalidFields() {
return Stream.of(
Arguments.of(Book::setName, "", "empty"),
Arguments.of(Book::setName, "a", "too short"),
Arguments.of(Book::setName, "a".repeat(1000), "too long")
// ...
);
}