i have few methods that need to be iterated in new transactions but facing errors when i try to write unit tests for them
@Inject
private SchedulerUtil schedulerUtil;
@Inject
private CompletableSelfReferralFilter filter;
@Inject
private SelfReferralAutoCloseHandler handler;
@Inject
private DemandForCareDAO demandForCareDAO;
@Inject
private AutoClosedSelfReferralInformationHelper autoClosedSelfReferralInformationHelper;
@Inject
private TransactionExecutor transactionExecutor;
private int closedCount = 0;
private int totalCount = 0;
private Stopwatch stopwatch;
private DateTime initialStartedTime;
private DateTime lastProcessedReferralDateTime;
@Override
public void schedule(SelfReferralAutoCloseConfigurationData configuration)
{
stopwatch = Stopwatch.createStarted();
lastProcessedReferralDateTime = autoClosedSelfReferralInformationHelper.getCreatedDateTime(configuration);
initialStartedTime = lastProcessedReferralDateTime;
long pageTotalCount;
long pageCount;
do
{
final long[] pageTotalCountHolder = new long[1];
final long[] pageCountHolder = new long[1];
transactionExecutor.runInNewReadOnlyTransaction(() -> {
final Page<DemandForCare> demandForCarePage = loadDemandForCarePage(configuration);
List<DemandForCare> openRequests = demandForCarePage.getContent();
pageTotalCountHolder[0] = demandForCarePage.getTotal();
pageCountHolder[0] = demandForCarePage.getContent().size();
complete(configuration, openRequests);
});
pageTotalCount = pageTotalCountHolder[0];
pageCount = pageCountHolder[0];
}
while (
schedulerUtil.isWithinExecutionTimeLimit(configuration.getExecutionTime(), stopwatch.elapsed(TimeUnit.HOURS)) &&
pageTotalCount > pageCount);
infoLogs(configuration, pageTotalCount == pageCount);
totalCount = 0;
closedCount = 0;
}
private void complete(SelfReferralAutoCloseConfigurationData configuration, List<DemandForCare> openRequests)
{
transactionExecutor.runInNewReadOnlyTransaction(() -> {
System.out.println("Processing " + openRequests.size() + " self referrals to complete.");
for (DemandForCare dfc : openRequests)
{
try
{
if (filter.filter(dfc, configuration))
{
handler.autoComplete(dfc.getId(), configuration.getCloseReason(), configuration.getMode());
closedCount++;
LOG.debug("Completed referral Id: {}", dfc.getId());
}
}
catch (Exception e)
{
if (e.getMessage() != null)
{
LOG.debug("Couldn't complete referral Id: {} and throws the error as {}", dfc.getId(), e.getMessage());
}
else
{
LOG.debug("Couldn't complete referral Id: {} and throws the error as ", dfc.getId(), e);
}
}
totalCount++;
lastProcessedReferralDateTime = dfc.getCreationInfo().getCreatedDateTime();
}
});
}
private Page<DemandForCare> loadDemandForCarePage(SelfReferralAutoCloseConfigurationData configurationData)
{
return demandForCareDAO.findInProgressSelfReferralsCreatedAfter(lastProcessedReferralDateTime,
new PagingParameters(0,
configurationData.getPageSize(),
Collections.emptyList()));
}
public class TransactionExecutor {
private static TransactionExecutor instance;
public TransactionExecutor() {
}
public static TransactionExecutor getInstance() {
return instance;
}
@Inject
public void setInstance(TransactionExecutor transactionExecutor) {
instance = transactionExecutor;
}
@Transactional(
propagation = Propagation.REQUIRES_NEW
)
public void runInNewTransaction(ThrowingRunnable<Exception> runnable) {
ExceptionWrapper.uncheck(runnable);
}
@Transactional(
propagation = Propagation.REQUIRES_NEW
)
public <V> V runInNewTransaction(Callable<V> callable) {
return ExceptionWrapper.uncheck(callable);
}
@Transactional(
readOnly = true,
propagation = Propagation.REQUIRES_NEW
)
public void runInNewReadOnlyTransaction(ThrowingRunnable<Exception> runnable) {
ExceptionWrapper.uncheck(runnable);
}
@Transactional(
readOnly = true,
propagation = Propagation.REQUIRES_NEW
)
public <V> V runInNewReadOnlyTransaction(Callable<V> callable) {
return ExceptionWrapper.uncheck(callable);
}
@Transactional(
propagation = Propagation.REQUIRED
)
public void runInTransaction(ThrowingRunnable<Exception> runnable) {
ExceptionWrapper.uncheck(runnable);
}
@Transactional(
propagation = Propagation.REQUIRED
)
public <V> V runInTransaction(Callable<V> callable) {
return ExceptionWrapper.uncheck(callable);
}
}
These methods are the focus of unit tests and my current test looks like this
@Mock
private SelfReferralAutoCloseHandler handler;
@Mock
private DemandForCareDAO demandForCareDAO;
@Mock
private CompletableSelfReferralFilter filter;
@Mock
private TermDAO termDAO;
@Mock
private AutoClosedSelfReferralInformationHelper autoClosedSelfReferralInformationHelper;
@Mock
private SchedulerUtil schedulerUtil;
@Mock
private TransactionExecutor transactionExecutor;
@InjectMocks
private SelfReferralAutoCloseScheduler scheduler;
@BeforeMethod
public void setUp()
{
MockitoAnnotations.initMocks(this);
when(autoClosedSelfReferralInformationHelper.getCreatedDateTime(any())).thenReturn(new DateTime());
// reset(transactionExecutor);
// when(transactionExecutor.runInNewReadOnlyTransaction(any(Callable.class)))
// .thenAnswer(invocation -> {
// Callable<?> callable = invocation.getArgument(0);
// return callable.call();
// });
}
@Test
public void testAutoComplete_whenReachingExecutionTime_thenCallingToComplete() throws Exception {
when(schedulerUtil.isWithinExecutionTimeLimit(anyInt(), anyLong())).thenReturn(true).thenReturn(false);
final SelfReferralAutoCloseConfigurationData configurationData = mock(SelfReferralAutoCloseConfigurationData.class);
when(configurationData.getExecutionTime()).thenReturn(1);
final Term closeReason = mock(Term.class);
when(configurationData.getCloseReason()).thenReturn(closeReason);
when(closeReason.getValue()).thenReturn("closeReason");
when(configurationData.getMode()).thenReturn(SelfReferralAutoClosureSchedulerMode.CLOSE_PAST_REFERRALS);
when(configurationData.getPageSize()).thenReturn(3);
List<DemandForCare> demandForCares = List.of(
getDemandForCare(getDFCId("123")),
getDemandForCare(getDFCId("234")),
getDemandForCare(getDFCId("345"))
);
Page<DemandForCare> demandForCarePage = new PageImpl<>(demandForCares, 9, 0);
List<DemandForCare> demandForCares2 = List.of(
getDemandForCare(getDFCId("1231")),
getDemandForCare(getDFCId("2341")),
getDemandForCare(getDFCId("3451"))
);
Page<DemandForCare> demandForCarePage2 = new PageImpl<>(demandForCares, 6, 0);
doReturn(Collections.singletonList(closeReason)).when(termDAO).findByValues(any(String[].class));
doReturn(demandForCarePage, demandForCarePage2).when(demandForCareDAO).findInProgressSelfReferralsCreatedAfter(any(DateTime.class), any(PagingParameters.class));
when(filter.filter(any(DemandForCare.class), any(SelfReferralAutoCloseConfigurationData.class))).thenReturn(true);
doNothing().when(handler).autoComplete(any(DemandForCare.Id.class), any(Term.class), any(SelfReferralAutoClosureSchedulerMode.class));
ArgumentCaptor<Callable<?>> callableCaptor = ArgumentCaptor.forClass(Callable.class);
// Capture the lambda passed to transactionExecutor
doAnswer(invocation -> {
Callable<?> callable = callableCaptor.getValue();
return callable.call();
}).when(transactionExecutor).runInNewReadOnlyTransaction(callableCaptor.capture());
scheduler.schedule(configurationData);
verify(transactionExecutor, times(4)).runInNewReadOnlyTransaction(any(Callable.class));
verify(handler, times(3)).autoComplete(any(DemandForCare.Id.class), eq(closeReason), eq(SelfReferralAutoClosureSchedulerMode.CLOSE_PAST_REFERRALS));
}
I have tried different ways to make the mocks work for transactionExecutor but seems like its trying to match the argument passed into the lambda instead of executing them. since a good portion of methods are not executed because they are inside the thread and because of that verifies fail. Following is the error stack i received after executing the code.
Argument(s) are different! Wanted:
transactionExecutor.runInNewReadOnlyTransaction(
<any java.util.concurrent.Callable>
);
-> at se.cambio.platform.sdk.server.util.TransactionExecutor.runInNewReadOnlyTransaction(TransactionExecutor.java:84)
Actual invocations have different arguments:
transactionExecutor.runInNewReadOnlyTransaction(
se.cambio.referral.taskservice.selfreferral.SelfReferralAutoCloseScheduler$$Lambda$202/0x0000011f63366650@119290b9
);
-> at se.cambio.referral.taskservice.selfreferral.SelfReferralAutoCloseScheduler.schedule(SelfReferralAutoCloseScheduler.java:70)
at se.cambio.platform.sdk.server.util.TransactionExecutor.runInNewReadOnlyTransaction(TransactionExecutor.java:84)
at se.cambio.referral.taskservice.selfreferral.SelfReferralAutoCloseSchedulerTest.testSchedule_whenExecutionTimeLimitExceeded_thenProcessingStops_test(SelfReferralAutoCloseSchedulerTest.java:99)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
at org.testng.SuiteRunner.run(SuiteRunner.java:364)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
at org.testng.TestNG.runSuites(TestNG.java:1049)
at org.testng.TestNG.run(TestNG.java:1017)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:65)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)
how can i fix this?
edit : also included dependency injections of fields
Your lambdas do not return anything. Consequently, they cannot be assigned to the SAM type Callable<V> (method V call()).
It looks like you are calling a different overload: TransactionExecutor.runInNewReadOnlyTransaction(ThrowingRunnable)
This code looks fishy as well – you want to access the arguments through invocation instead:
doAnswer(invocation -> {
Callable<?> callable = callableCaptor.getValue();
return callable.call();
}).when(transactionExecutor).runInNewReadOnlyTransaction(callableCaptor.capture());
I don't see the captor used elsewhere for verification, so you might want to remove it altogether.
The fix is probably trivial – migrate to the correct type and access the actual method argument:
// Invoke the lambda passed to transactionExecutor
doAnswer(invocation -> {
invocation.<ThrowingRunnable>getArgument(0).run();
return null;
})
.when(transactionExecutor)
.runInNewReadOnlyTransaction(any(ThrowingRunnable.class));
scheduler.schedule(configurationData);
verify(transactionExecutor, times(4))
.runInNewReadOnlyTransaction(any(ThrowingRunnable.class));