I have a method in a class called "HttpResponseHelper" that I am trying to Unit Test when it throws a JsonProcessingException, but I was having difficulties getting it do so:
private static void populateHTTPResponseWithData(ObjectNode httpResponse)
{
ObjectMapper mapper = new ObjectMapper();
responseMapData.keySet().forEach(item -> {
try
{
httpResponse.put(item, mapper.writeValueAsString(responseMapData.get(item)));
}
catch (JsonProcessingException e)
{
LOGGER.error("Json Processing Exception", e);
}
});
}
The httpResponse argument is type ObjectNode (Jackson library), and then inside the method body a mapper object is created from the ObjectMapper class.
The resonseMapData is a ConcurrentHashMap> from a class called "MessageProcessResults". It looks like here its looping through the keySet and inserting a String for the Key Value pair inside of the httpResponse argument.
I tried using mockito on mapper to return a malformed JSON, but it looks like it writes the value as a String and passes each time.
Does anyone have any suggestions or is there a simple way to do this? Thank you for taking the time to read this question and possibly help me :D
You can also extend JsonProcessingException for your tests:
private static class MockJsonProcessingException extends JsonProcessingException {
public MockJsonProcessingException(String message) {
super(message);
}
}
And then in your test:
var exception = new MockJsonProcessingException("Because of protected constructors");
when(objectMapper.writeValueAsString(thing)).thenThrow(exception);
EDIT: Note this assumes you're using dependency injection and injecting a mock ObjectMapper into your object, as is necessary to make this case testable.
The #HarryQ answer wont work as JsonProcessingException has a package protected constructor.
For those methods that throw this you can use InputCoercionException. It extends from JsonProcesssingException and allows something like, ugly, yeah I know, but it works:
when(mockObjectMapper.writeValueAsString(any())).thenThrow(new InputCoercionException(null, "my mock exception", null, null));
When you are doing unit test, you don't focus on how the underlying code would create an exception, but how your code deal with such an exception. In that regards, you can mock the ObjectNode object, and ask it to throw an exception whenever put method is called.
#Test
public void someTest(){
Object mockObject = Mockito.mock(ObjectNode.class);
Mockito.when(mockObject.put()).thenThrow(new JsonProcessingException ("my mock exception"));
functionUndertest(mockObject); //this is where you inject your mock function. In your case you are expecting a error message to be printed.
}
Related
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));
}
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".
I want to have 100% coverage on the method toString overriden with Jackson JSON.
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return "";
}
}
I can make a test that can coverage the most of the code except the catch block.
#Test
public void testToString() {
TestClass testClass = new TestClass();
String expected = "{\"testAttr\":null}";
assertEquals(expected, testClass.toString());
}
How could I make a test that covers the catch block?
Of course you cold try to trigger that exception somehow with setting the enclosing object to some weird kind of state. But a better way to achieve full code coverage is mocking the mapper and making it throw the desired exception. Generally the steps that you need are:
Transfer the creation of ObjectMapper to a new method 'getObjectMapper'. During runtime it will decide if it returns a real or a fake mapper.
In your test inject a fake ObjectMapper and make 'getObjectMapper' return the fake mapper instead of a new mapper.
When the writeValueAsString method is called make it throw a JsonProcessingException
Run your test
In theory you could manually create the fake ObjectMapper e.g. by creating a subclass of it but that's not the recommended way. I would recommend using a mocking framework. A mocking framework lets you express things like "when method A is called then throw exception B".
You can define some attributes to your class and assert the return of the string.
You've already tested the null object.
Raise the exception and see if it handles it.
Like:
when(mapper.writeValueAsString(any(Object.class))).thenThrow(new JsonProcessingException("Error"){});
And also helpful link
I guess this is a junit and Logback problem. In my project, Logging is done through slf4j. The logging implementation is Logback.
So I have a class:
#Component
#Slf4j
public class A {
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String DEFAULT_REPLY = "just continue...";
public String doSomething(Object value) {
try {
return objectMapper.methodAbc(value);
} catch (JPException e) {
log.error("Exception while processing value", e);
return DEFAULT_REPLY;
}
}
}
and its test class
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Before
public void init() {
processor = new A();
}
#Test
public void test() throws Exception {
ObjectMapper mockMapper = mock(ObjectMapper.class);
JP mockJp = mock(JP.class);
Exception thrownException = new JPException(mockJp, null);
when(mockMapper.methodAbc(any())).thenThrow(thrownException);
String result = processor.doSomething("abc");
assertTrue(result.equals("just continue..."));
}
}
I don't have any problem with the test itself. Just as you can see, in the test, the JPException will be printing out on the log, because it's intentionally thrown.
When I debug through logs, there're too many this kind of expected exceptions, I'm just wondering is there a way to remove them from logs? And of course still print other exceptions which is not expected.
Logback has the functionality to support filters and evaluations based on certain logic.
This link is probably what you could be looking for :
https://logback.qos.ch/manual/layouts.html#Evaluators
You can configure your logback to do or not do certain action if it is an instance of any exceptio - in your case JPException
Try this:
Create a mock for the log.
Inject the mock into the class being tested.
Assert that the error method on the mocked log object was called.
I'm testing a certain class. This class is internally instantiating a "GetMethod" object that gets passed to a "HttpClient" object that gets injected into the tested class.
I'm mocking the "HttpClient" class, but I would need to modify the behaviour of one method of the "GetMethod" class too. I'm playing with ArgumentCaptor but I don't seem to be able to get a hold of the instantiated object in the "when" call.
Example:
HttpClient mockHttpClient = mock(HttpClient.class);
ArgumentCaptor<GetMethod> getMethod = ArgumentCaptor.forClass(GetMethod.class);
when(mockHttpClient.executeMethod(getMethod.capture())).thenReturn(HttpStatus.SC_OK);
when(getMethod.getValue().getResponseBodyAsStream()).thenReturn(new FileInputStream(source));
Response:
org.mockito.exceptions.base.MockitoException:
No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()
You cant use when on getMethod, because getMethod is not a mock. It is still real object created by your class.
ArgumentCaptor has quite different purpose. Check section 15 here.
You could make your code more testable. Generally, classes that are creating new instances of other classes are difficult to test. Put some factory to this class for creating get/post methods, then in test mock this factory, and make it mock get/post methods.
public class YourClass {
MethodFactory mf;
public YourClass(MethodFactory mf) {
this.mf = mf;
}
public void handleHttpClient(HttpClient httpClient) {
httpClient.executeMethod(mf.createMethod());
//your code here
}
}
Then in test you can do:
HttpClient mockHttpClient = mock(HttpClient.class);
when(mockHttpClient.executeMethod(any(GetMethod.class)).thenReturn(HttpStatus.SC_OK);
MethodFactory factory = mock(MethodFactory.class);
GetMethod get = mock(GetMethod.class);
when(factory.createMethod()).thenReturn(get);
when(get.getResponseBodyAsStream()).thenReturn(new FileInputStream(source));
UPDATE
You can also try some nasty hack, and Answer and accessing GetMethod's private parts ;) by reflection. (This is really nasty hack)
when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
GetMethod getMethod = (GetMethod) invocation.getArguments()[0];
Field respStream = HttpMethodBase.class.getDeclaredField("responseStream");
respStream.setAccessible(true);
respStream.set(getMethod, new FileInputStream(source));
return HttpStatus.SC_OK;
}
});
Ok, this is how I've solved it. A little bit convoluted but couldn't find any other way.
In the test class:
private GetMethod getMethod;
public void testMethod() {
when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new ExecuteMethodAnswer());
//Execute your tested method here.
//Acces the getMethod here, assert stuff against it.
}
private void setResponseStream(HttpMethodBase httpMethod, InputStream inputStream) throws NoSuchFieldException, IllegalAccessException {
Field privateResponseStream = HttpMethodBase.class.getDeclaredField("responseStream");
privateResponseStream.setAccessible(true);
privateResponseStream.set(httpMethod, inputStream);
}
private class ExecuteMethodAnswer implements Answer {
public Object answer(InvocationOnMock invocation) throws FileNotFoundException,
NoSuchFieldException, IllegalAccessException {
getMethod = (GetMethod) invocation.getArguments()[0];
setResponseStream(getMethod, new FileInputStream(source));
return HttpStatus.SC_OK;
}
}