Sprng batch stepscope reader test with Mockito - org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read

57 views Asked by At

I am testing @stepscope JDBCCUrsorIteamReader but getting below error. I am opening the reader before start reading but still its giving reader is not opened. I want to mock database calls.

How can i make it work while mocking the DB calls

org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read.

reader code

@Bean
@StepScope
public JdbcCursorItemReader<LimsExternalLinkDTO> findExternalLinksReader(@Value("#{jobParameters['bookId'] != null ? jobParameters['bookId'] : 0}") int bookId) {
    JdbcCursorItemReader<LimsExternalLinkDTO> reader = new JdbcCursorItemReader<>();
    reader.setDataSource(dataSource);
    reader.setSql(FIND_EXTERNAL_LINKS);
    reader.setPreparedStatementSetter(ps -> {
        ps.setInt(1, bookId);
        ps.setInt(2, bookId);
    });
    reader.setRowMapper(new BeanPropertyRowMapper<>(LimsExternalLinkDTO.class));
    reader.setFetchSize(BATCH_SIZE);
    reader.setQueryTimeout(REQUEST_TIMEOUT);
    return reader;
}

Reader Test code

@ContextConfiguration
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
        StepScopeTestExecutionListener.class })
@RunWith(SpringJUnit4ClassRunner.class)
class ReaderConfigurationTest {

    @Autowired
    private JdbcCursorItemReader<LimsExternalLinkDTO> itemReader;

    @BeforeEach
    void setUp() throws SQLException {
        itemReader = new JdbcCursorItemReader<>();

       // itemReader.setDataSource(ds);

    }

    @AfterEach
    void tearDown() {
    }

    @Test
    void testUsesCurrentTransaction() throws Exception {
        DataSource ds = Mockito.mock(DataSource.class);
        Connection con = Mockito.mock(Connection.class);
        Mockito.when(con.getAutoCommit()).thenReturn(false);
        PreparedStatement ps = Mockito.mock(PreparedStatement.class);
        Mockito.when(con.prepareStatement("select foo from bar", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.HOLD_CURSORS_OVER_COMMIT))
                .thenReturn(ps);
        Mockito.when(ds.getConnection()).thenReturn(con);
        Mockito.when(ds.getConnection()).thenReturn(con);
        con.commit();
        PlatformTransactionManager tm = new JdbcTransactionManager(ds);
        TransactionTemplate tt = new TransactionTemplate(tm);
        final JdbcCursorItemReader<LimsExternalLinkDTO> reader = new JdbcCursorItemReader<>();
        reader.setDataSource(new ExtendedConnectionDataSourceProxy(ds));
        reader.setUseSharedExtendedConnection(true);
        reader.setSql("select foo from bar");
        final ExecutionContext ec = new ExecutionContext();
        ec.put("externalLinks",mockedLimsExternalLinkData());
        tt.execute(status -> {
            reader.open(ec);

            try {
                reader.read();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            reader.close();
            return null;
        });
    }

    public List<LimsExternalLinkDTO> mockedLimsExternalLinkData() {
        List<LimsExternalLinkDTO> linksList = new ArrayList<>();

        LimsExternalLinkDTO limsExternalLink = new LimsExternalLinkDTO();
        limsExternalLink.setExternalLink("https://www.google.com");
        limsExternalLink.setId(1);
        limsExternalLink.setBookName("Test Book");
        limsExternalLink.setStatus(null);
        limsExternalLink.setBookId(316);
        limsExternalLink.setChapterId(6);
        limsExternalLink.setChapterName("Abacavir");
        limsExternalLink.setSectionId(1);
        limsExternalLink.setSectionName("Section1");
        limsExternalLink.setFieldId(1);
        linksList.add(limsExternalLink);

        return linksList;
    }

}

logs-

11:23:07.607 [Test worker] DEBUG org.springframework.jdbc.support.JdbcTransactionManager - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
11:23:07.613 [Test worker] DEBUG org.springframework.jdbc.support.JdbcTransactionManager - Acquired Connection [Mock for Connection, hashCode: 718512571] for JDBC transaction
11:23:07.617 [Test worker] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
11:23:07.630 [Test worker] DEBUG org.springframework.transaction.support.TransactionTemplate - Initiating transaction rollback on application exception
java.lang.RuntimeException: org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read.
    at com.lexi.lims.reports.externallinks.batch.reader.ReaderConfigurationTest.lambda$testUsesCurrentTransaction$0(ReaderConfigurationTest.java:89)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
    at com.lexi.lims.reports.externallinks.batch.reader.ReaderConfigurationTest.testUsesCurrentTransaction(ReaderConfigurationTest.java:83)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy2.stop(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read.
    at org.springframework.batch.item.database.AbstractCursorItemReader.doRead(AbstractCursorItemReader.java:497)
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:93)
    at com.lexi.lims.reports.externallinks.batch.reader.ReaderConfigurationTest.lambda$testUsesCurrentTransaction$0(ReaderConfigurationTest.java:87)
    ... 85 common frames omitted
11:23:07.637 [Test worker] DEBUG org.springframework.jdbc.support.JdbcTransactionManager - Initiating transaction rollback
11:23:07.638 [Test worker] DEBUG org.springframework.jdbc.support.JdbcTransactionManager - Rolling back JDBC transaction on Connection [Mock for Connection, hashCode: 718512571]
11:23:07.638 [Test worker] DEBUG org.springframework.jdbc.support.JdbcTransactionManager - Releasing JDBC Connection [Mock for Connection, hashCode: 718512571] after transaction
1

There are 1 answers

0
Mahmoud Ben Hassine On

The code you shared autowires the JdbcCursorItemReader in the test class (this means the reader should be defined as a bean in the test context and managed by Spring), and at the same time creates the bean manually in the setUp method. This is incorrect, the manual creation of the bean should be removed.

The example of testing scoped beans in the reference documentation does not create the bean manually with the new operator: Testing Step-Scoped Components.

That said, the error you have seems to happen before the test is called where the reader is indeed opened before starting to read data.