Intermittent unit test failure due to CompletableFuture.thenAccept not running - java

I have a method that collects data from multiple factories using CompletableFuture.supplyAsync like below
#RunWith(MockitoJUnitRunner.StrictStubs.class)
public class TestThenApply {
#Mock private Factory1 factory1;
#Mock private Factory2 factory2;
#Mock private Factory3 factory3;
#Test
public void case1() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", "factory2");
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case2() throws Exception {
when(factory1.create()).thenThrow(new RuntimeException("Test!!"));
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", null);
expected.put("factory2", "factory2");
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case3() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenThrow(new RuntimeException("Test!!"));
when(factory3.create()).thenReturn("factory3");
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", null);
expected.put("factory3", "factory3");
assertEquals(expected, getModel());
}
#Test
public void case4() throws Exception {
when(factory1.create()).thenReturn("factory1");
when(factory2.create()).thenReturn("factory2");
when(factory3.create()).thenThrow(new RuntimeException("Test!!"));
Map<String, Object> expected = new HashMap<>();
expected.put("factory1", "factory1");
expected.put("factory2", "factory2");
expected.put("factory3", null);
assertEquals(expected, getModel());
}
private Map<String, Object> getModel() {
Map<String, Object> map = new HashMap<>();
CompletableFuture<Void> factory1Future = supplyAsyncWithCallable("factory1", () -> factory1.create())
.thenAccept(result -> map.put("factory1", result));
CompletableFuture<Void> factory2Future = supplyAsyncWithCallable("factory2", () -> factory2.create())
.thenAccept(result -> map.put("factory2", result));
CompletableFuture<Void> factory3Future = supplyAsyncWithCallable("factory3", () -> factory3.create())
.thenAccept(result -> map.put("factory3", result));
CompletableFuture.allOf(factory1Future, factory2Future, factory3Future).join();
return map;
}
private <T> CompletableFuture<T> supplyAsyncWithCallable(String widgetName, Callable<T> widgetModelBuilder) {
return CompletableFuture.supplyAsync(() -> {
try {
return widgetModelBuilder.call();
} catch (Exception e) {
return null;
}
});
}
}
I have 4 unit tests which tests below scenarios
When all 3 factories succeeds // Happy case
When factory1 fails but other 2 factories succeed
When factory2 fails but other 2 factories succeed
When factory3 fails but other 2 factories succeed
90% of the time all these test cases pass however one test case fails (assertion error) randomly sometimes.
I suspect this might be because thenAccept does not run before the main thread ends. I was able to get it working all the time by moving all the thenAccept logic after the join(). However I don't want to do that unless I have to. Any help is appreciated.

Thanks to #ernest_k
Using HashMap was the issue here. Replaced it with thread safe map and now the test cases pass consistently.

Related

Java : newSingleThreadScheduledExecutor is not giving expected result when used parallel to akka framework

Using Akka framework for my use case where I created one SupervisorActor and two child actors now parallel to that I have token service which needs to update my cache before the expiry please find the code :
public class TokenCacheService {
final Logger logger = LoggerFactory.getLogger(TokenCacheService.class);
private static final String KEY = "USER_TOKEN";
private LoadingCache<String, String> tokenCache;
private final ScheduledExecutorService cacheScheduler;
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MyCacheRefresher-pool-%d").setDaemon(true)
.build();
public UserTokenCacheService(CacheConfig cacheConfig) {
cacheScheduler = Executors.newSingleThreadScheduledExecutor(threadFactory);
buildCache(cacheConfig);
}
public String getToken() {
String token = StringUtils.EMPTY;
try {
token = tokenCache.get(KEY);
} catch (ExecutionException ex) {
logger.debug("unable to process get token...");
}
return token;
}
private void buildCache(CacheConfig cacheConfig) {
tokenCache = CacheBuilder.newBuilder()
.refreshAfterWrite(4, "HOURS")
.expireAfterWrite(5, "HOURS")
.maximumSize(2)
.build(new CacheLoader<String, String>() {
#Override
#ParametersAreNonnullByDefault
public String load(String queryKey) {
logger.debug("cache load()");
return <token method call which return token>
}
#Override
#ParametersAreNonnullByDefault
public ListenableFutureTask<String> reload(final String key, String prevToken) {
logger.debug("cache reload()");
ListenableFutureTask<String> task = ListenableFutureTask.create(() -> return <token method call which return token>);
cacheScheduler.execute(task);
return task;
}
});
cacheScheduler.scheduleWithFixedDelay(() -> tokenCache.refresh(KEY), 0,
4, "HOURS");
}
}
It is working fine with test class :
public static void main(String[] args) throws InterruptedException {
TokenCacheService userTokenCacheService = new TokenCacheService();
while(true){
System.out.println(tokenCacheService.getToken());
Thread.sleep(180000);
}
}
above method printing correct logs as after 4 hours which is expected but when I run the above code with my actual application (with Akka-actors) I can only see the first log cache load() apart from this it isn't printing further log for reload the cache.
Please suggest what am doing wrong here.
I tweaked the code little bit by setting the priority to 1 and replaced scheduleWithFixedDelay with scheduleAtFixedRate
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MyCacheRefresher-pool-%d")
.setPriority(1)
.build();
public UserTokenCacheService(CacheConfig cacheConfig) {
idsTokenApplication = new IdsTokenApplication();
cacheScheduler = Executors.newSingleThreadScheduledExecutor(threadFactory);
buildCache(cacheConfig);
}
cacheScheduler.scheduleAtFixedRate(() -> tokenCache.refresh(KEY), 0,
cacheConfig.getReloadCache(), TimeUnit.valueOf(cacheConfig.getReloadCacheTimeUnit()));

Junit How to mock namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs))

i am writing test cases for repository classes were i am not able to cover some of lines in repository classes. i need to achieve 85% of code coverage and its mandatory in my case,Please suggest me something
My actual method
public Map<String, String> getProductFamily(List<String> itmNms) {
Map<String, String> productFamilyMap=new HashMap<String, String>();
try {
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
String sql = "some query";
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue("itmNms", itmNms);
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
Map<String, String> productFamily = new HashMap<>();
while (rs.next()) {
productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
}
return productFamily;
});
}catch (Exception e) {
LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
}
return productFamilyMap;
}
Test case for above method
#Test
public void getProductFamily() {
List<String> itmNms = new ArrayList<String>();
itmNms.add("A-SPK-NAMED-USER");
oracleRepo.getProductFamily(itmNms);
Map<String, String> mp = new HashMap<String, String>();
Assert.assertNull(mp);
}
By writing above test cases i am able to cover code coverage till line no 6 below lines i am not able to cover due to below statements
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) ->{}
Can some one suggest how can i achieve code coverage for above method as 100%.
In cases like that, you need to "manually invoke" the code in lambda. This can be performed with Mockito.doAnswer(...) functionality of Mockito framework. The example (suitable for Mockito 2+):
Mockito.doAnswer(invocationOnMock -> {
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);
Then you can verify productFamilyMap for populated key-value pair.
If you'd still have troubles with it, you can share your code (e.g. via Github) and I'll try to help you with it.
EDIT: Initially, I didn't notice the thing that NamedParameterJdbcTemplate is created manually with new, and it's kinda hard to mock it. In this case, it's better to refactor your production code a bit - you can create NamedParameterJdbcTemplate object as bean (like you probably did with raw JdbcTemplate) and then inject it into your class (and ofc remove the line where you're creating it with new). Then the things become trivial.
#Component
public class OracleRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(OracleRepository.class);
#Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; //created as bean in configuration class
public Map<String, String> getProductFamily(List<String> itmNms) {
Map<String, String> productFamilyMap=new HashMap<String, String>();
try {
String sql = "some query";
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
namedParameters.addValue("itmNms", itmNms);
productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
Map<String, String> productFamily = new HashMap<>();
while (rs.next()) {
productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
}
return productFamily;
});
}catch (Exception e) {
LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
}
return productFamilyMap;
}
}
The test class remains unchanged:
#RunWith(MockitoJUnitRunner.class)
public class OracleRepositoryTest {
#InjectMocks
private OracleRepository oracleRepo;
#Mock
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Test
public void getProductFamily() {
List<String> itmNms = new ArrayList<>();
itmNms.add("A-SPK-NAMED-USER");
Mockito.doAnswer(invocationOnMock ->{
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn("A-SPK-NAMED-USER");
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn("SPKCLD");
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);
Map<String, String> productFamilyMap = oracleRepo.getProductFamily(itmNms);
Assert.assertEquals("SPKCLD", productFamilyMap.get("A-SPK-NAMED-USER"));
}
}
The above answer is accurate and is working. I tried in my project too.
Let me try to explain how this works.
We are saying when mocked JDBCTemplate query() method is called, then we want to invoke our own lambda expression with some mocking done, like we first create a mocked result set, and mocks some of its getString methods. Next we capture the third argument of mocked invocation which is result set extractor. Now from here we simply return this extractor extract data method with our mocked result set which will now be invoked.
So in essence we are calling the original extract data method with our mocked result set.
Mockito.doAnswer(invocationOnMock -> {
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);
ResultSetExtractor<Map<String, String>> resultSetExtractor =
invocationOnMock.getArgument(2);
return resultSetExtractor.extractData(resultSet);
}).when(namedParameterJdbcTemplate).query(
Mockito.anyString(),
Mockito.any(MapSqlParameterSource.class),
Mockito.any(ResultSetExtractor.class)
);

Testing method having CompletableFuture.allOf using mockito

I have a method called doParallelThings:
public Dummy doParallelThings(Map<String, String> mapp) throws Exception {
Dummy dummy = new Dummy();
CompletableFuture<Ans1> one = firstService.getOne(mapp.get("some1"), mapp);
CompletableFuture<Ans2> two = secondService.getTwo(headersMap.get("some2"), mapp);
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(one, two);
try {
combinedFuture.get();
dummy.setOne(one.get());
dummy.setTwp(two.get());
} catch (Throwable e) {
}
return dummy;
}
Code works fine but when I'm trying to test it,
combinedFuture.get(); goes to infinite loop.
Unit test is as below:
#Mock
private CompletableFuture<Void> ans;
#Test
public void testDoParallelThings() throws Exception {
PowerMockito.mockStatic(CompletableFuture.class);
PowerMockito.when(CompletableFuture.allOf(any())).thenReturn(ans);
when(ans.get()).thenReturn(null);
Dummy dummy = dummyService. doParallelThings(mockMap);
assertNotNull(dummy);
}
I have also added #RunWith(PowerMockRunner.class)
#PrepareForTest({CompletableFuture.class}) above the test class.
What am I missing?
when(firstService.getOne(any(), any())).thenReturn(CompletableFuture.completedFuture(mockOne));
solved my problem

Consumer unit test with poll() never receives anything

Consider the following code:
#Test(singleThreaded = true)
public class KafkaConsumerTest
{
private KafkaTemplate<String, byte[]> template;
private DefaultKafkaConsumerFactory<String, byte[]> consumerFactory;
private static final KafkaEmbedded EMBEDDED_KAFKA;
static {
EMBEDDED_KAFKA = new KafkaEmbedded(1, true, "topic");
try { EMBEDDED_KAFKA.before(); } catch (final Exception e) { e.printStackTrace(); }
}
#BeforeMethod
public void setUp() throws Exception {
final Map<String, Object> senderProps = KafkaTestUtils.senderProps(EMBEDDED_KAFKA.getBrokersAsString());
senderProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
senderProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
final ProducerFactory<String, byte[]> pf = new DefaultKafkaProducerFactory<>(senderProps);
this.template = new KafkaTemplate<>(pf);
this.template.setDefaultTopic("topic");
final Map<String, Object> consumerProps = KafkaTestUtils.consumerProps("sender", "false", EMBEDDED_KAFKA);
this.consumerFactory = new DefaultKafkaConsumerFactory<>(consumerProps);
this.consumerFactory.setValueDeserializer(new ByteArrayDeserializer());
this.consumerFactory.setKeyDeserializer(new StringDeserializer());
}
#Test
public void testSendToKafka() throws InterruptedException, ExecutionException, TimeoutException {
final String message = "42";
final Message<byte[]> msg = MessageBuilder.withPayload(message.getBytes(StandardCharsets.UTF_8)).setHeader(KafkaHeaders.TOPIC, "topic").build();
this.template.send(msg).get(10, TimeUnit.SECONDS);
final Consumer<String, byte[]> consumer = this.consumerFactory.createConsumer();
consumer.subscribe(Collections.singleton("topic"));
final ConsumerRecords<String, byte[]> records = consumer.poll(10000);
Assert.assertTrue(records.count() > 0);
Assert.assertEquals(new String(records.iterator().next().value(), StandardCharsets.UTF_8), message);
consumer.commitSync();
}
}
I am trying to send a message to a KafkaTemplate and read it again using Consumer.poll(). The test framework I am using is TestNG.
Sending works, I have verified that using the "usual" code I found in the net (register a message listener on a KafkaMessageListenerContainer).
Only, I never receive anything in the consumer. I have tried the same sequence (create Consumer, poll()) against a "real" Kafka installation, and it works.
Hence it looks like there is something wrong with the way I set up my ConsumerFactory? Any help would be greatly appreciated!
You need to use
EMBEDDED_KAFKA.consumeFromAnEmbeddedTopic(consumer, "topic");
before publishing records via KafkaTemplate.
And then in the end of test for verification you need to use something like this:
ConsumerRecord<String, String> record = KafkaTestUtils.getSingleRecord(consumer, "topic");
You can also use it the way you do, only what you are missing is a ConsumerConfig.AUTO_OFFSET_RESET_CONFIG as an earliest, because the default one is latest. That way a consumer added to the topic later won't see any records published before.

How can I test exception in completable future?

I have been converting some code to be asynchronous. The original unit test used the annotation #Test(expected = MyExcpetion.class) but I don't think this will work because the exception I want to assert on is wrapped in java.util.concurrent.ExcutionException . I did try calling my future like this but my assertion is still failing and I don't love that I had to add in return null
myApiCall.get(123).exceptionally((ex) -> {
assertEquals(ex.getCause(),MyCustomException.class)
return null
}
I also tried this flavor but still not working
myApiCall.get(123).exceptionally((ex) -> {
assertThat(ex.getCause())
.isInstanceOF(MyException.class)
.hasMessage("expected message etc")
return null;
}
My API just throws exception if it can't find id. How should I be properly testing this? Can I use that original annotation in anyway?
my api call reaches out to db when run. In this test I am setting up my future to return an error so it doesn't actually try to communicate with anything. the code under test looks like this
public class myApiCall {
public completableFuture get(final String id){
return myService.getFromDB(id)
.thenApply(
//code here looks at result and if happy path then returns it after
//doing some transformation
//otherwise it throws exception
)
}
}
in the unit test I force myService.getFromDB(id) to return bad data so I can test exception and also keep this a unit test don't reach out to db etc.
Let's assume your API throws if called with 0:
public static CompletableFuture<Integer> apiCall(int id) {
return CompletableFuture.supplyAsync(() -> {
if (id == 0) throw new RuntimeException("Please not 0!!");
else return id;
});
}
You can test that it works as expected with the following code (I'm using TestNG but I suspect it won't be too difficult to translate into a JUnit test):
#Test public void test_ok() throws Exception {
CompletableFuture<Integer> result = apiCall(1);
assertEquals(result.get(), (Integer) 1);
}
#Test(expectedExceptions = ExecutionException.class,
expectedExceptionsMessageRegExp = ".*RuntimeException.*Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
result.get();
}
Note that the second test uses the fact that the ExecutionException message will contain the original exception type and message and captures the expectation with a regex. If you can't do that with JUnit, you can call result.get() in a try/catch block and call throw e.getCause(); in the catch block. In other words, something like this:
#Test(expectedExceptions = RuntimeException.class,
expectedExceptionsMessageRegExp = "Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
try {
result.get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
You can try also alternative option:
import org.hamcrest.core.IsInstanceOf;
import org.junit.rules.ExpectedException;
public class Test() {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void myApiCallTest() {
thrown.expect(ExcutionException.class);
thrown.expectCause(IsInstanceOf.instanceOf(MyException.class));
thrown.expectMessage("the message you expected");
myApiCall.get("");
}
}
Assuming that:
public class myApiCall {
public completableFuture get(final String id) {
// ...
throw new ExcutionException(new MyException("the message you expected"))
}
}
Assume that you have a class and you want to test a method which returns a completable future:
public class A {
private final Api api;
public A(Api api) { this.api = api;}
public CompletableFuture<Void> execute(Integer input) {
final CompletableFuture<Void> future = api.execute(input)
.thenApplyAsync(result -> doSomething())
.exceptionally(ex -> doFailure());
return future;
}
}
To test the execution of "doSomething()" then you may use mockito and do the following:
// prepare test
final Api api = mock(Api.class)
final A a = new A(api);
when(api.execute(any(Integer.class)))
.thenReturn(CompletableFuture.completedFuture(null));
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
...
To test "doFailure" do the following:
when(api.execute(any(Integer.class))).thenAnswer(answer -> {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
});
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
assertTrue(result.isCompletedExceptionally());
that is easy thing doing in junit-4. Are you remember the #RunWith annotation? Yes, write your own TestRunner to intercept the exception before the junit expected exception processor is invoked, for example:
public class ConcurrentRunner extends BlockJUnit4ClassRunner {
public ConcurrentRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test,
Statement next) {
return super.possiblyExpectingExceptions(
method, test, throwingActualException(next)
);
}
private Statement throwingActualException(Statement next) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
next.evaluate();
} catch (ExecutionException | CompletionException source) {
throw theActualExceptionOf(source);
}
}
private Throwable theActualExceptionOf(Exception source) {
return source.getCause() != null ? source.getCause() : source;
}
};
}
}
just annotated with #RunWith(ConcurrentRunner.class) on the test, you needn't change your test code at all. for example:
#RunWith(ConcurrentRunner.class)
public class ConcurrentExpectedExceptionTest {
#Test(expected = IllegalArgumentException.class)
public void caughtTheActualException() throws Throwable {
myApiCall().join();
}
private CompletableFuture<Object> myApiCall() {
return CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
}
}

Categories

Resources