Mock consumers interface in subscribe method - java

I am trying to mock two consumer interfaces inside subscribe method
ConsumerService.java
#Autowired
ConsumerDao consumerDao;
public void insertStatus(){
SchedularStatus schedularStatus = new SchedularStatus();
Mono<Object> savedConsumer = consumerDao.save(schedularStatus);
savedConsumer.subscribe(success -> Log.info("consumer data saved")),
(error-> Log.error("Error while saving schedular data));"
}
Below Junit test code I have tried
ConsumerServiceTest.java
#ExtendWith(MockitoExtension.class)
class ConsumerServiceTest {
#InjectMock
ConsumerService consumerService;
#Mock
Mono<Object> mono;
#Mock
ConsumerDao consumerDao;
#Test
void testInsertStatus(){
Mockito.when(consumerDao.save(any(SchedularStatus))).thenReturn(mono);
doAnswer(answer -> {
Consumer<Object> consumer1 = answer.getArgument(0);
Consumer<Object> consumer2 = answer.getArgument(1);
consumer1.accept(new Object());
consumer2.accept(new Object());
return null;
}).when(mono).subscribe(any(Consumer.class), any(Consumer.class));
Mockto.verify(consumerDao).save(any(SchedularStatus.class));
}
But I am getting Nullpointer Exception
java.lang.NullPointerException : errorConsumer
at java.util.Objects.requireNonNull(Ojbects.java:228)
at reactor.core.publisher.Mono.subscribe(Mono.java:4278)

Don't mock non-service classes. Mocking Monos or Optionals or CompletableFutures almost always leads to problems (I have also seen real test code which created and set up mocks for Lists and Maps). Simply return a real instance:
Mockito.when(consumerDao.save(any(SchedularStatus)))
.thenAnswer(a -> Mono.just(new Object()));
And a second test which sets up the mock to return a failed Mono (Mono.error(new Exception())).
But then again, your test is not really testing anything (or performing very questionable actions), because:
You mock the method that you are verifying
Your mocked mono is successful and failed at the same time
You are never calling a method on your class under test
Your consumers don't do anything really, so why bother calling them?

Your NPE comes from your mock configuration :
doAnswer(answer -> {
Consumer<Object> consumer1 = answer.getArgument(0);
Consumer<Object> consumer2 = answer.getArgument(1);
consumer1.accept(new Object());
consumer2.accept(new Object());
return null; // You return null instead of a valid Disposable
}).when(mono).subscribe(any(Consumer.class), any(Consumer.class));
subscribe method return a Disposable. You have to provide one.

Related

Mockito spy not mocking internal call to mocked method

I have a class that calls out to a third party system to get data that I want to mock using Mockito. Here is some pseudo-code of how my class is setup.
// class I will be spying
public class SomeService {
// method I want to full test
public List<String> methodA(List<String> list) {
... omitted code ...
// internal call to method in the same class that will be mocked
innerList = methodB(list);
... omitted code ...
}
// method that I want to mock/spy
public List<String> methodB(List<String> list) {
//details omitted since it should be mocked any never called
}
}
Here is my test code. I setup my spy in a BeforeEach method to make sure that Mock is configured. When I directly call the mocked method, everything works as expected. When I call
public class SomeServiceTest{
private SomeService service;
#BeforeEach
public void init() {
service = Mockito.spy(SomeService.class);
// also tried with creating a new object but has same failure conditions
//service = Mockito.spy(new SomeService());
// mocking methodB with answer details omitted to reduce clutter
Mockito.doAnswer(..detail omitted..).when(service).methodB(Mockito.anyList());
}
//testing directly calling mocked method that returns mocked data
#Test
public void testMockedMethod() throws Exception {
List<String> list = //setup details ignored
List<String> results = service.methodB(list);
// follow up asserts work
}
//testing indirectly calling mocked method that call actual method code
#Test
public void testMethodA() throws Exception {
List<String> list = //setup details ignored
// NullPointer in methodB since mocking is ignore
List<String> results = service.methodA(list);
// assertions never tested since NPE was thrown
}
}
Does anyone know why when I directly call the mocked method, it returns the doAnswer; but when I indirectly call from within the mocked/spied object it ignores the fact that the method is mocked and calls the original method?
It should work. It seems to me that the NPE exception is because there are some bugs in your codes which already happen before it really executes the stubbed method . What is the stacktrack that you get ?
On the other hand, you can also add some println just before and after calling the stubbed method to verify the codes is really can execute up to that point :
public List<String> methodA(List<String> list) {
.......
System.out.println("before methodB");
innerList = methodB(list);
System.out.println("after methodB which return " + innerList);
....
}

How to test for Exception in #Async method?

How can I assert that a certain exception is thrown inside an #Async method?
Because the following exception is caught by Springs SimpleAsyncUncaughtExceptionHandler.
#Service
public class Service {
#Async
public void run() {
throw new RuntimeException();
}
}
public class Test {
#Test
public void test() {
assertDoesNotThrow(() -> service.run()); //this always passes
}
}
If it is possible for your case, separate testing of asynchronicity and the actual unit of work. E.g. write test that will execute (no 'Async' functionality) Service.run() and assert that no/any/some exceptions are thrown.
In second test (utilizing #Async execution) you could test for the actual unit of work i.e. use your Spring provided bean and test for e.g.:
Awaitility.await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(() -> runAnyCodeThatChecksTheResultOfYourServiceRunMethod());
Another method might be to replace the return type of the Service.run() method to java.util.concurrent.Future, Spring will then re-throw the exception. From AsyncExecutionAspectSupport.handleError javadoc:
"If the return type of the method is a {#link Future} object (again, if applicable), the original exception can be propagated by just throwing it at the higher level. However, for all other cases, the exception will not be transmitted back to the client."
import static org.awaitility.Awaitility.await;
Awaitility.await().atMost(5, TimeUnit.SECONDS)
.untilAsserted(() -> assertThrows(RuntimeException.class, () -> service.run()));

Mockito mock method based on another mock method

Let's say that I have a some service class
class Service<T> {
T get(int id);
void save();
}
In my unit tests I mock both methods of this service using mockito. But there is a case when method save should be mocked based on mocked get method. For example if method get is called with an argument which is equal to 2 then method save should throw some exception.
I went through Mockito documentation but seems like have not found any solutions yet.
Any ideas how I can achieve this use case with Mockito?
1) what you are trying to do is calling a test case from from a test case.
2) Mocking is used mainly for testing the endpoint, may be for controllers.So if your save functions needs to called again or you have to reuse the code for other test case, you should do that, it has no problem.
But what you are saying is totally against the programming paradigms for mock test cases.
In my opinion needing a mocking like that should be avoided, but if you insist you can leverage the doAnswer() method for detailed mock handling.
Basically, you use two Answer instances. One that sets a flag when the method is called with 2, the other one resetting the flag.
Another answer reacts to that flag and throws an exception if needed.
private boolean throwExceptionNext;
#Test
public void test() {
Service mock = Mockito.mock(Service.class);
Mockito.doAnswer((__) -> {
throwExceptionNext = false;
return new Object();
}).when(mock).get(Mockito.anyInt());
Mockito.doAnswer((__) -> {
throwExceptionNext = true;
return new Object();
}).when(mock).get(2);
Mockito.doAnswer((__) -> {
if (throwExceptionNext)
throw new RuntimeException();
return null;
}).when(mock).save();
mock.get(3);
mock.save();
mock.get(2);
try {
mock.save();
Assert.fail();
} catch (RuntimeException e) {
}
mock.get(3);
mock.save();
}

Mock a method that returns a future to throw an exception

I'm using Java and Mockito to mock some methods for unit testing. I want to mock the producer in the below code so that I can test for the log messages that are sent when the exception is thrown. I tried mocking the future however I get the error that the future.get() method cannot be mocked, and the RecordMetadata class is final and cannot be mocked. Any help would be greatly appreciated.
The producer in the below example is a KafkaProducer.
public void send(Log log){
Future<RecordMetadata> future = producer.send(new ProducerRecord<(this.topic, record));
try {
RecordMetadata recordMetadata = send.get();
} catch (InterruptedException e) {
LOG.error("Sending the message to kafka was interrupted. "+e);
}
}
Kafka supplies a MockProducer class that you can inject into your class for testing. You can set the producer in your class to an instance of MockProducer in a JUnit #Before-annotated setup method. From the referenced documentation for MockProducer:
A mock of the producer interface you can use for testing code that uses Kafka.
By default this mock will synchronously complete each send call successfully. However it can be configured to allow the user to control the completion of the call and supply an optional error for the producer to throw.
You can use the #errorNext method to supply the exception that you want to throw in your test scenario.
It is difficult to be precise here without seeing your test code. Two issues
1) RecordMetadata cannot be used a Mockito mock, this is a known limitation of Mockito. Instead you can create a dummy instance of RecordMetadata using its public constructor.
2) KafkaProducer can be mocked by Mockito, but you need a two stage approach. Firstly the mock KafkaProducer returns a Future, and secondly that Future is also a mock that returns some known value.
public class ServiceTest {
#Mock
private KafkaProducer<String, Integer> producer;
#Mock
private Future<RecordMetadata> future;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void success() throws Exception {
RecordMetadata dummyRecord = new RecordMetadata(null, 0L, 0L, 0L, 0L, 0, 0);
when(producer.send(any())).thenReturn(future);
when(future.get()).thenReturn(dummyRecord);
producer.send(null);
}
#Test
public void timeout() throws Exception {
when(producer.send(any())).thenReturn(future);
when(future.get()).thenThrow(new TimeoutException("timeout"));
producer.send(null);
}
}

Presenter unit test with RxJava CompositeSubscription

I would like to create a test for my Presenter class but I'm having problems with the CompositeSubscription instance inside the Presenter itself. When I run the test I'm getting this error:
java.lang.NullPointerException
at rx.subscriptions.CompositeSubscription.add(CompositeSubscription.java:60)
at com.example.Presenter.addSubscription(Presenter.java:67)
at com.example.Presenter.getGummyBears(Presenter.java:62)
This is roughly my Presenter class:
public class Presenter {
CompositeSubscription compositeSubscription = new CompositeSubscription();
//creation methods...
public void addSubscription(Subscription subscription) {
if (compositeSubscription == null || compositeSubscription.isUnsubscribed()) {
compositeSubscription = new CompositeSubscription();
}
compositeSubscription.add(subscription);
}
public void getGummyBears() {
addSubscription(coreModule.getGummyBears());
}
}
The CoreModule is an interface (part of a different module) and there is another class CoreModuleImpl in which are located all retrofit API calls and their conversion to Subscriptions.
Something like:
#Override public Subscription getGummyBears() {
Observable<GummyBears> observable = api.getGummyBears();
//a bunch of flatMap, map and other RxJava methods
return observable.subscribe(getDefaultSubscriber(GummyBear.class));
//FYI the getDefaultSubscriber method posts a GummyBear event on EventBus
}
Now what I want to do is to test the getGummyBears() method.
My test method looks like this:
#Mock EventBus eventBus;
#Mock CoreModule coreModule;
#InjectMock CoreModuleImpl coreModuleImpl;
private Presenter presenter;
#Before
public void setUp() {
presenter = new Presenter(coreModule, eventBus);
coreModuleImpl = new CoreModuleImpl(...);
}
#Test
public void testGetGummyBears() {
List<GummyBears> gummyBears = MockBuilder.newGummyBearList(30);
//I don't know how to set correctly the coreModule subscription and I'm trying to debug the whole CoreModuleImpl but there are too much stuff to Mock and I always end to the NullPointerException
presenter.getGummyBears(); //I'm getting the "null subscription" error here
gummyBears.setCode(200);
presenter.onEventMainThread(gummyBears);
verify(gummyBearsView).setGummyBears(gummyBears);
}
I already saw many test examples from different projects but no one is using this Subscription approach. They just return the Observable which is consumed directly inside the presenter. And in that case I know how a test has to be written.
What's the correct way to test my situation?
Looks like coreModule.getGummyBears() is returning null. Just step through with debug and it should be pretty clear. When using mocking frameworks you can get null returned from method calls on a mocked object when you haven't specified what the method call should return on that mocked object.
As Dave mentioned, you need to mock the return value of CoreModule.getGummyBears. One strange thing is that you are not using the CoreModuleImpl that is being created. Instead, you're passing coreModule to the presenter's constructor.
You can mock getGummyBears() by doing something like this:
when(coreModule.getGummyBears()).thenReturn(MockBuilder.newGummyBearList(30);
Then the specific error you are encountering should be resolved. It doesn't look like you need CoreModuleImpl for this specific test case.

Categories

Resources