У меня есть метод пакетного обновления таблицы.
Проблема в том, что когда я пытаюсь вызвать блок кода ниже, он дает приведение класса исключение,
when(session.doWork(any())).thenAnswer(invocation -> {
Consumer<Connection> consumer = invocation.getArgument(0);
consumer.accept(mockConnection);
return null;
});
java.lang.ClassCastException: com.paypal.compliance.data.process.eftns.dao.impl.EFTReportsDAO$$Lambda$9/170106909 cannot be cast to java.util.function.Consumer
при отладке в intellij я обнаружил, что invocation имеет 2 аргумента arg$1 и arg$2, arg$1 — это оператор SQL, а arg$2 — список EFTReportsDO, как извлечь их по отдельности?
Ниже приведены метод и тестовый пример.
@Transactional(value = "test", propagation = Propagation.REQUIRES_NEW)
public void mergeAllEftReports(List<EFTReportsDO> instanceList) {
String updateSql = "UPDATE EFT_XYZ " +
"SET " +
"COMMENTS=?, " +
"PP_BATCH_SOURCE=?, " +
"WHERE " +
"PP_BATCH_ID=? " +
"AND REPORT_ID=?";
try {
Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> {
try (PreparedStatement preparedStatement = connection.prepareStatement(updateSql)) {
for (EFTReportsDO entity : instanceList) {
preparedStatement.setString(1, entity.getComments());
preparedStatement.setString(2, entity.getPpBatchSource());
preparedStatement.setString(3, entity.getId().getPpBatchId());
preparedStatement.setString(4, entity.getId().getReportId());
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
}
});
} finally {
entityManager.flush();
entityManager.close();
}
}
@Test
public void testMergeAllEftReports() throws SQLException {
// Arrange
List<EFTReportsDO> instanceList = new ArrayList<>();
EFTReportsDO eftReportsDO = new EFTReportsDO(); // create an instance of EFTReportsDO
instanceList.add(eftReportsDO);
when(entityManager.unwrap(Session.class)).thenReturn(session);
Connection connection = mock(Connection.class);
PreparedStatement preparedStatement = mock(PreparedStatement.class);
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
// Act & Assert
when(session.doWork(any())).thenAnswer(invocation -> {
Consumer<Connection> consumer = invocation.getArgument(0);
consumer.accept(mockConnection);
return null;
});
// Call the method under test
eftReportsDAO.mergeAllEftReports(instanceList);
verify(preparedStatement, times(1)).executeBatch();
// Verify that flush and close methods are called on the entityManager
verify(entityManager, times(1)).flush();
verify(entityManager, times(1)).close();
}




Ваша лямбда не может быть преобразована в Consumer<Connection>, она должна соответствовать типу SAM, ожидаемому Session.doWork, а именно Work
doAnswer((invocation) -> {
Work jdbcWork = invocation.getArgument(0);
jdbcWork.execute(connection);
return null;
}).when(session).doWork(any());
Примечание: session.doWork возвращает void, поэтому я использовал doAnswer — когда, а не когда — thenAnswer.
В общем, вы не можете выполнить кастинг между двумя разными интерфейсами SAM, даже если их структура идентична:
interface StringConsumer1 {
void execute(String s1);
}
interface StringConsumer2 {
void execute(String s1);
}
@Test
void testLambdaConversionFails() {
Throwable thrown = catchThrowable(() -> {
StringConsumer1 l1 = (aaa) -> System.out.println(aaa);
StringConsumer2 l2 = (StringConsumer2) l1;
});
assertThat(thrown).isInstanceOf(ClassCastException.class);
}
В вашем случае Consumer<Connection> не идентичен Work (название метода и проверенные исключения различаются)
Код немного сложно протестировать, поскольку существует анонимная реализация Work, которая вызывает session.doWork. Аргументом этого метода также является имитируемый connection класс, и все зависит от этого макетного connection класса.
Следовательно, в вашем тесте вам необходимо записать лямбда-выражение и проверить его функциональность. Я написал минимальное количество кода, чтобы продемонстрировать это.
Класс, который нужно протестировать;
public class MainClass {
@Autowired
private EntityManager entityManager;
public void executeQuery() {
Session session = entityManager.unwrap(Session.class);
try {
session.doWork(connection -> {
System.out.println("calling the lambda implementation");
PreparedStatement preparedStatement = connection.prepareStatement("update some_table set name = ? where id = ? ");
preparedStatement.setString(1, "new_name");
preparedStatement.setLong(2, 1L);
preparedStatement.execute();
});
} catch (Exception ex) {
System.out.println("Error occurred");
} finally {
entityManager.flush();
entityManager.close();
}
}
}
Тест;
@ExtendWith(MockitoExtension.class)
class TestHibernateWork {
@Mock
EntityManager entityManager;
@InjectMocks
MainClass underTest;
@Test
void process() {
Session session = mock(Session.class);
when(entityManager.unwrap(any())).thenReturn(session);
// this will help capture the real anonymous implementation of Work; which is the lambda expression
ArgumentCaptor<Work> workArgumentCaptor = ArgumentCaptor.forClass(Work.class);
// Capture the lambda when doWork is called
doNothing().when(session).doWork(workArgumentCaptor.capture());
// call the method to test the lambda
underTest.executeQuery();
// get the captured implementation of Work, which is the lambda expression.
Work work = workArgumentCaptor.getValue();
try {
Connection connection = mock(Connection.class);
PreparedStatement preparedStatement = mock(PreparedStatement.class);
when(connection.prepareStatement(any())).thenReturn(preparedStatement);
// call the real implementation of work captured previously using the mock connection
work.execute(connection);
// verify the calls made on prepareStatement
verify(preparedStatement).setString(1, "new_name");
verify(preparedStatement).setLong(2, 1L);
verify(preparedStatement).execute();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
спасибо за разъяснение, пробуя этот подход
Спасибо за разъяснение . Это сработало успешно