Mockito throw exception for KafkaTemplate.send using thenThrow - java

I'm testing following function.
public boolean produceNumberOfPeople(NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO) {
final ProducerRecord<Integer, Integer> record = new ProducerRecord<>(
KafkaConfig.NUMBER_OF_PEOPLE_BY_PLACE_TOPIC,
numberOfPeopleInPlaceDTO.getId(),
numberOfPeopleInPlaceDTO.getNumberOfPeople());
try {
kafkaTemplate.send(record).get(2, TimeUnit.SECONDS);
return true;
}
catch (ExecutionException | TimeoutException | InterruptedException e) {
return false;
}
}
Here is test code.
#Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class)))
.thenReturn(listenableFuture);
Mockito.when(listenableFuture.get(2,TimeUnit.SECONDS))
.thenThrow(TimeoutException.class);
Assert.assertFalse(placeService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}
And I defined following variables.
#Autowired
private PlaceService placeService;
#MockBean
private PlaceRepository placeRepository;
#MockBean
private KafkaTemplate<Integer, Integer> kafkaTemplate;
#MockBean
private ListenableFuture listenableFuture;
The problem is that kafkaTemplate.send(record).get(2,TimeUnit.SECONDS) doesn't throw exception. So test keep failing.
Please advice anything I 'm missing.

I will recommend to create failed ListenableFuture object with exception instead of Mock
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException())
And then just return this in mock
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(listenableFuture);
So when the get method is called it throws ExecutionException
This method returns the value if it has been set via set(Object), throws an ExecutionException if an exception has been set via setException(Throwable), or throws a CancellationException if the future has been cancelled.

I am glad you solved by passing the instances to the constructor.
However, instead of creating a proper constructor, and instantiate the placeService in the test method itself, I would use another approach.
As best practice, it is recommendable to have specific setXXX methods to pass the instances, for example in your case, in PlaceService class you should have something like this:
public void setListenableFuture(ListenableFuture listenableFuture) {
this.listenableFuture = listenableFuture;
}
public void setKafkaTemplate(KafkaTemplate<Integer, Integer> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
Then, you can invoke them either in your test method (in your case produceNumberOfPeopleTest) or, even better in a specific setUp one, like this:
#Before
public void setUp() throws Exception {
placeService.setListenableFuture(listenableFuture);
placeService.setKafkaTemplate(kafkaTemplate);
}
In this way, you can leave Mock objects and placeService as members of your test class, so that JUnit and Spring Runner will have the responsibility of instantiating those objects and inject them in placeService, then you can just customize the mock behaviours each test methods you will write, according to your needs.
In my experience, I found this quite helpful, as each object involved does its proper job. Even in terms of test implementation and maintainability, you will not have to repeat the same code at each test method. For example, think about what could happen if at the certain point you have to change that constructor, in that case you will have to change all methods where you used it as well. Don't you think?

The problem was that PlaceService was not using the mock instance of KafkaTemplate. So I passed mock instance to the constructor of PlaceService manually. Now test is passed.
Here is new test code.
#Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
PlaceService testPlaceService = new PlaceServiceImpl(null,kafkaTemplate);
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException());
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(future);
Assert.assertFalse(testPlaceService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}

Related

Mockito: Mock object which is no member but is created inline

I have a class which does the following:
public class Transformer {
public void transform(final Car car) throws IOException {
switch (car.getType()) {
case OFFROAD:
OffroadCar offroadCar = new OffroadTransformer().transform(car);
// do something with offorad car
break;
...
}
}
}
I have a test class:
public class TransformerTest {
#InjectMocks
private Transformer transformer;
#Mock
private OffroadTransformer offroadTransformer;
#BeforeEach
public void setup()
MockitoAnnotations.initMocks(this);
}
#Test
public void testTransform() throws IOException {
final Car car = new Car(OFFROAD);
when(offroadTransformer.transform(any(Car.class))).thenReturn(new OffroadCar());
transformer.transform(car);
// make some verifictations
}
}
My problem now is that the when is not working. The real offroadTransformer.transform is called instead of the mock. So my assumption is that the mock is not working because the OffroadTransformer is no member of the class Transformer and the instance is created inline.
Is that correct?
If yes: How can I anyway mock it?
If no: What else could be the cause?
The problem is that the OffroadTransformer object being used is not mocked. Your test setup is creating a mock in the transformer field, but this mock is not the one being used by the method, and that confirms your hypothesis.
The setup you're using works with classes that have mocked objects as instance fields, like this:
public class Transformer {
//injected, initialized inline, etc.
private OffroadTransformer transformer;
public void transform(final Car car) throws IOException {
switch (car.getType()) {
case OFFROAD:
OffroadCar offroadCar = this.transformer.transform(car);
// do something with offorad car
break;
...
}
}
}
Into such a class, Mockito would inject the mock and the method execution would use that mock created by Mockito.
If you do not want to use this set up, then you may want to look into something like mocking the constructor of your OffroadTransformer.
On a side note, however, it's rather common practice for factory classes such as OffroadTransformer to have no state and to be used as singletons. It's therefore more natural to follow the setup mentioned above and let Mockito handle the injection for you.

Runtime exceptions in Java 8 function

I'm using Java 8 Functions and converters and have the following:
Main class
public final class MainClass {
public MainClass(
final Function<InputModel, OutputModel> businessLogic,
final Converter<Input, InputModel> inputConverter,
final Converter<OutputModel, Output> outputConverter) {
this.businessLogic = requireNonNull(businessLogic, "businessLogic is null.");
this.inputConverter = requireNonNull(inputConverter, "inputConverter is null.");
this.outputConverter = requireNonNull(outputConverter, "outputConverter is null.");
}
/**
* Request Handler.
*/
public Output handleRequest(final Input input) {
requireNonNull(input, "input is null.");
log.info("input request: {}", input);
try {
return inputConverter
.andThen(businessLogic)
.andThen(outputConverter)
.apply(input);
} catch (final Exception ex) {
throw new InternalServiceException(ex.getMessage(), ex);
}
}
}
Unit test
public final class TestClass {
#Mock
private Function<InputModel, OutputModel> mockedBusinessLogic;
#Mock
private Converter<Input, InputModel> mockedInputConverter;
#Mock
private Converter<OutputModel, Output> mockedOutputConverter;
private MainClass mainClass = new MainClass(mockedBusinessLogic, mockedInputConverter, mockedOutputConverter);
#Test
public void handleRequest_SHOULD_throwException_WHEN_inputConverter_throwsException() {
final RuntimeException inputConverterException = new NullPointerException(EXCEPTION_MESSAGE);
// what should I mock here? apply or convert.. apply for `Converter` seems to be deprecated.
when(mockedInputConverter.convert(input))
.thenThrow(inputConverterException);
final Exception exception = assertThrows(
InternalServiceException.class,
() -> mainClass.handleRequest(input)
);
assertThat(exception.getMessage(), is(EXCEPTION_MESSAGE));
assertThat(exception.getCause(), is(inputConverterException));
}
}
The above assertions fail.
I expect that if the inputConverter throws an exception, the catch block in handleRequest would wrap it to InternalServiceException, but it doesn't seem to be happening.
Any help?
How do I actually write unit tests for handleRequest method? I want to test the behavior when either of inputConveter, businessLogic or outputConveter throws exception.
Everything in your code is a mock. When you call andThen on your mocked inputConverter, then either null or a new mock instance is returned (depending on configuration). Each andThen will return a new instance with the chained converters (at least that is what I assume)
Make sure you mock all required methods, or better, use real objects instantiated from real classes.
Setting breakpoints and then debugging should help you find the issue. If you set in your try-block, and then single-step through the code, you will see that the way mocks are used in your code will not work. You could also save each result of andThen in a variable and then check in the debugger what type each has. I'm pretty sure it will either be null or "Mock for class X".

Mockito verify unit test - Wanted but not invoked. Actually, there were zero interactions with this mock

At first I want to sorry for my english.
I started to make some unit tests (i've never done this before, i'm a new guy in programming).
I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have
"Wanted but not invoked. Actually, there were zero interactions with this mock."
Error and I don't know what to do.
This is my method code (in KitchenService class):
public Product addProduct(Product content) {
ObjectMapper objectMapper = new ObjectMapper();
String mediaJSON = null;
String authorJSON = null;
String productKindsJSON = null;
try {
mediaJSON = objectMapper.writeValueAsString(content.getMedia());
authorJSON = objectMapper.writeValueAsString(content.getAuthor());
productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
} catch (JsonProcessingException e) {
logger.log(e.getMessage());
}
Item item = new Item()
.withPrimaryKey("id", UUID.randomUUID().toString())
.with("name", content.getName())
.with("calories", content.getCalories())
.with("fat", content.getFat())
.with("carbo", content.getCarbo())
.with("protein", content.getProtein())
.with("productKinds", productKindsJSON)
.with("author", authorJSON)
.with("media", mediaJSON)
.with("approved", content.getApproved());
Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
logger.log(save + " created");
return content;
}
And this is test code:
#Test
public void addProduct() throws Exception {
KitchenService instance = mock(KitchenService.class);
Product expectedProduct = new Product();
expectedProduct.setName("kaszanka");
expectedProduct.setCalories(1000);
expectedProduct.setFat(40.00);
expectedProduct.setCarbo(20.00);
expectedProduct.setProtein(40.00);
expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
expectedProduct.setApproved(false);
Author expectedAuthor = new Author();
expectedAuthor.setId("testID");
expectedAuthor.setName("Endrju Golota");
expectedProduct.setAuthor(expectedAuthor);
Media expectedMedia = new Media();
expectedMedia.setMediaType(MediaType.IMAGE);
expectedMedia.setName("dupajasia");
expectedMedia.setUrl("http://blabla.pl");
expectedProduct.setMedia(expectedMedia);
verify(instance, times(1)).addProduct(expectedProduct);
}
This is what I got after test:
Wanted but not invoked:
kitchenService.addProduct(
model.kitchen.Product#a0136253
);
-> at service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.
Can someone tell me what im doing wrong?
What you should mock and verify is the databaseController dependency:
#Test
public void addProduct() throws Exception {
KitchenService instance = new KitchenService(); // you should create the class under test
DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller
instance.setController(controller); // inject the mock
...
// Act
instance.addProduct(expectedProduct);
// Assert
verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));
}
You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.
Mocking is a tool that you only use for dependencies of the class that is being tested.
It appears that your test does not care about the Author, Media, and Product objects,
these are just dependencies of the method you want to test;
mock them.
Organization will greatly help your test;
do something like this:
public class TestKitchenService
{
private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
... use constants for other values as well. The value of the constant does not matter.
#InjectMocks
private KitchenService classToTest;
private InOrder inOrder;
#Mock
private Author mockAuthor;
#Mock
private DatabaseController mockDatabaseController;
#Mock
private Logger mockLogger;
#Mock
private Media mockMedia;
#Mock
private Product mockProduct;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockAuthor);
verifyNoMoreInteractions(mockDatabaseController);
verifyNoMoreInteractions(mockLogger);
verifyNoMoreInteractions(mockMedia);
verifyNoMoreInteractions(mockProduct);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doReturn(mockAuthor).when(mockProduct).getAuthor();
doReturn(mockMedia).when(mockProduct).getMedia();
doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();
... doReturns for the other product values.
inOrder = inOrder(
mockAuthor,
mockDatabaseController,
mockLogger,
mockMedia,
mockProduct);
ReflectionTestUtils.setField(
classToTest,
"databaseController",
mockDatabaseController);
ReflectionTestUtils.setField(
classToTest,
"logger",
mockLogger);
}
#Test
public void addProduct_success()
{
final Product actualResult;
actualResult = classToTest.addProduct(mockProduct);
assertEquals(
mockProduct,
actualResult);
inOrder.verify(mockProduct).getMedia();
inOrder.verify(mockProduct).getAuthor();
inOrder.verify(mockProduct).getProductKinds();
inOrder.verify(mockProduct).getName();
... inOrder.verify for the other product values.
inOrder.verify(mockDatabaseController).saveProduct(
eq(PRODUCT_TABLE),
any(Item.class));
}
}
The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.
While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.

Mocking not able to mock method call

I am trying to mock a call in my test but I am getting a error as its calling the real method than mocking it.
This is my method
#Value("${omega.aws.nonprod-profile}")
private String nonProdProfile;
#Autowired
AwsService awsService;
public List<SecurityGroup> getAllSecurityGroups() {
AmazonEC2 ec2 = configSetter();
return awsService.getAllSecurityGroups(ec2);
}
protected AmazonEC2 configSetter() {
ProfileCredentialsProvider credentials = new ProfileCredentialsProvider(nonProdProfile);
ClientConfiguration clientCfg = new ClientConfiguration();
clientCfg.setProxyHost(this.proxyHost);
clientCfg.setProxyPort(Integer.valueOf(proxyPort));
clientCfg.setProtocol(Protocol.HTTPS);
return new AmazonEC2Client(credentials, clientCfg);
}
Here is my test class
#InjectMocks
private AwsConfigurationLocal subject;
#Mock
private AwsService awsService;
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
securityGroup = new SecurityGroup();
List<SecurityGroup> result = Collections.singletonList(securityGroup);
Mockito.when(awsService.getAllSecurityGroups(ec2)).thenReturn(result);
List<SecurityGroup> actual = subject.getAllSecurityGroups();
assertThat(actual, CoreMatchers.is(equals(result)));
}
The test actually calls the protected method configSetter and fails when setting a proxy. Help me understand what I am doing wrong here.
subject.getAllSecurityGroups(); calls real configSetter() which returns real AmazonEC2 which in turn is passed on to awsService.getAllSecurityGroups(ec2);. The parameter doesn't match your mock ec2 so the default mock implementation is returned (I guess it's null) as a actual.
So the issue is: there is nothing that would prevent real implementation of configSetter() to be called.
If you annotate subject with #Spy and do
Mockito.when(subject.configSetter()).then(ec2);
it should work as expected.
That being said there's a lot of set up done only to check simple invocation being delegated. This is due intermixing of two responsibility in AwsConfigurationLocal - creation of AmazonEC2Client and providing getAllSecurityGroups(). If you move the former into separate class (let's say AmazonEC2ClientFactor) everything should fall in place.
Try using powerMockito to return the mocked instance of AmazonEC2
#RunWith(PowerMockRunner.class)
#PrepareForTest({AwsConfigurationLocal.class})//assuming this is your test class
#InjectMocks
private AwsConfigurationLocal subject=PowerMockito.spy(AwsConfigurationLocal.class);//or try Mockito.spy instead, whichever works
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
PowerMockito.doReturn(ec2).when(subject).configSetter().withNoArguments();
//your code
}

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

Categories

Resources