Извлеките поля переменных Consumer, имитируя выражение Lambda

У меня есть метод пакетного обновления таблицы.

Проблема в том, что когда я пытаюсь вызвать блок кода ниже, он дает приведение класса исключение,

        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();
        }

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваша лямбда не может быть преобразована в 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 (название метода и проверенные исключения различаются)

Спасибо за разъяснение . Это сработало успешно

bon mohanty 29.09.2023 15:19

Код немного сложно протестировать, поскольку существует анонимная реализация 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);
        }
    }
}

спасибо за разъяснение, пробуя этот подход

bon mohanty 29.09.2023 15:20

Другие вопросы по теме