issue with mockito test for try catch block with slif4j Log - java

I am writting test for a try catch block, but I am quite confused about how to test the catch block...especially it uses slf4j to log the error.
addText here is the another method from the same class.
public class TextQueue {
public void addTextToQueue(final Text text) {
try {
if (text != null) {
addText(text);
}
} catch (final JsonProcessingException e) {
LOGGER.error("Error adding text to the queue : {}", e);
}
}
}
here is my test case
#RunWith(MockitoJUnitRunner.class)
public class TextQueueTest {
private org.slf4j.Logger LOGGER = LoggerFactory.getLogger(TextQueueTest.class);
private static final String MY_TEXT = "src/text.json";
private Text text;
private final ObjectMapper mapper = new JacksonConfig().dateAsStringObjectMapper();
#Mock
private TextQueue textQueue;
#Before
public void setUp() throws IOException {
text = mapper.readValue(new File(TextQueueTest.MY_TEXT), Text.class);
}
#Test
public void addTextToQueue() {
try{
textQueue = spy(textQueue);
textQueue.addTextToQueue(text);
}catch(final Exception e){
LOOGER.error("add text to queue threw an error" + e);
}
}
can anyone help me solve this problem?

First of all, you should really read a good tutorial about Mockito, like the one from vogella. You see, you are simply throwing together a lot of things that are non-sensical.
Like:
#Mock
private TextQueue textQueue;
to then have
textQueue = spy(textQueue);
within your test case. You should be really clear about this. A spy is build on a real instance of your class under test. Creating a spy that spies on a mock, as said: that makes no sense.
Then:
}catch(final Exception e){
Logger.error("add text to queue threw an error" + e);
Again, non-sensical. The whole idea of your unit tests is that they fail when something is wrong. When your production code throws unexpected exceptions, you don't log them, you just let them fail your test case in the end.
To answer the actual question: it looks like your production code is using a specific "constant" logger instance. Given that design, the only way to check your production code would be to:
make that LOGGER a mocked object
somehow inject it into an instance underTest of your production code class
trigger that method to test on underTest (and somehow force the method to throw an exception)
verify that the mocked LOGGER saw the expected call to error()
We can't give better advise, because your code input isn't sufficient, we don't really know what your production class is doing (for example: we don't know what LOGGER is, and where it is coming from. if it happens to be a static variable, then most likely, you can't get "control" over it with Mockito).
In any case, you probably actually need the spy concept. In order to test addTextToQueue() you need a way to invoke the "real" addTextToQueue() implementation, but the call to addTser() within needs to go to a mock (so that you can control what that call does).
But as said: start by really researching how Mockito works, instead of throwing together things that make no sense in some "trial and error" approach. Correct unit testing with mocking is complicated, you can't learn that by "trial and error".

Related

Powermockito/Java - Spying class under test to verify private method call

I'm writing unit tests in java using Mockito/PowerMockito, but on the test I'm working on, I can't get rid of this UnfinishedStubbingException.
The method I'm trying to test is private, so I use WhiteBoxImpl to invoke the method. Inside the method I invoke, a call is potentially made to another private method (call it pm2) in the class under test. I want to verify that pm2 is never called, so I make a spy for the class under test, and verify pm2 is never() called.
So far, this test has always thrown an UnfinishedStubbingException, but I can't figure out what part of my test Powermockito doesn't like. I have another (working) test that operates very similarly, except I don't need to verify the behavior of a method like pm2. So in this working case, I don't need to create a spy for the class under test. I believe my issue is somehow related to the spy, but I don't know of a way to test what I'm trying to test without it.
Here's what I have right now:
#Mock(name = "BO")
BO BOMock;
#Mock(name = "DAO")
DAOI DAOMock;
#InjectMocks
ServiceImpl service;
#Test
public void unitTest(){
MessageObject msg = new MessageObject();
Record recordMock = mock(Record.class);
MetaData metaDataMock = mock(MetaData.class);
doNothing().when(DAOMock).doAction(any(Param1.class), anyInt());
when(DAOMock.doOtherAction(any(Param1.class), eq(msg.getId()))).thenReturn(recordMock);
when(BOMock.getMetaData(anyInt(), anyInt()).thenReturn(metaDataMock);
ServiceImpl spy = PowerMockito.spy(this.service);
PowerMockito.doReturn(new Long(10)).when(spy, "checkDelay", recordMock, msg, metaDataMock);
Whitebox.invokeMethod(spy, "process", msg);
verify(recordMock, never()).getStatus();
}
Here's the method in the class ServiceImpl that I'm testing:
private BO BO = new BO();
private DAOI DAO = new DAO();
private void process(Message msg) {
try {
DAO.doAction(new Param1.class, msg.getId());
} catch(Exception e) {
logger.error("some message");
return;
}
Record record = null;
try {
int intParam1 = msg.getId();
int intParam2 = msg.getDifferentId();
MetaData metaData = BO.getMetaData(intParam1, intParam2);
record = DAO.loadRecord(new Param1(), msg.getId());
// checkDelay is a private method in ServiceImpl.java
long delayForMinutes = checkDelay(record, msg, metaData);
if(delayForMinutes > 0) {
// Control should reach here
logger.debug("some message");
return;
}
// Control should not reach here
if(Record != null && Record.getStatus() != CREATED) {
logger.debug("some message");
return;
}
// Perform various actions
} catch(Exception e) {
// Perform other various actions
}
}
When I run this test I get an UnfinishedStubbingException. The line at the top of the stack trace is:
DAO.doAction(new Param1.class, msg.getId());
The error message provides the following hints:
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
But I can't seem to figure out how any of them apply to my situation. Does anyone know what's going on behind the scenes to cause this error?
Thank you
The problem line is
when(DAOMock.doOtherAction(any(Param1.class), eq(msg.getId()))).loadRecord(recordMock);
You don't seem to have a then, thenReturn or thenThrow here. You always need to use one of those with when.

Custom #Annotation to report boolean at runtime

I'm searching around to see if what I want to accomplish is possible with annotations.
Basically, we have a bunch of TestNG test cases which we're micro managing
Example:
#Test
public void reportingTest(){
Assert.true(false);
}
The above would simply fail, but we wrap everything in an assertion try catch.
#Test
public void reportingTest(){
try {
Assert.true(false);
Report.batch(Enum.Pass, 106);
} catch (Throwable t) {
Report.batch(Enum.Fail, 106, "Test case has failed");
}
}
However, after hundreds of test-cases... having that try catch is super cumbersome.
I am trying to accomplish something like this
#Reporting(id=106)
#Test
public void reportingTest(){
Assert.true(false);
}
Inside of the annotation I would have the ability to capture the failed assertion and send a log off based on my id.
Thanks!
TestNG provides listeners and the one you are looking for may be the TestListener.
Your annotation will be available from there: tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Reporting.class).

Jmock/junit testing

If I have an SUT which handles an exception using a try/catch block, as follows:
public static void methodToBeTested() {
...
try {
desktop.browse(new URI("www.google.com"));
} catch (IOException e) {
//Display message to user and log out entry in app logs
}
...
}
Question is that should I test the condition from my unit tests that the IOException is thrown? (The method under test launches a URI in the default browser)
If yes, since I am not throwing the exception from this method, how do i unit test this condition when the desktop.browse() threw an IOException?
Any thoughts or suggestions? I am using JMock
Thanks!
Basically what you want to do is to
mockup Desktop and whenever you send a browse message to it (no matter what URI is used), instead of hitting that URI, it should throw an IOException.
I have used Jmock long time ago. JMock as far as I remember, has some limitations, for exmaple it does not provide a mechanism for mocking static methods. And I am not sure how easy it is to mock your browser class in jmock world.
However it is almost trivial to test this using jmockit, which supports all sorts of fancy mocking mechanisms (including static references, singletons etc). (I am mentioning jmockit because no matter what your browse class is, jmockit can mock it.)
Below is an excerpt from an example from their website:
package jmockit.tutorial.domain;
import org.apache.commons.mail.*;
import jmockit.tutorial.persistence.*;
import org.junit.*;
import mockit.*;
public final class MyBusinessService_ExpectationsAPI_Test
{
#Mocked(stubOutClassInitialization = true) final Database unused = null;
#Mocked SimpleEmail anyEmail;
#Test
public void doBusinessOperationXyz() throws Exception
{
final EntityX data = new EntityX(5, "abc", "abc#xpta.net");
final EntityX existingItem = new EntityX(1, "AX5", "someone#somewhere.com");
new Expectations() {{
(1) Database.find(withSubstring("select"), any);
result = existingItem; // automatically wrapped in a list of one item
}};
new MyBusinessService(data).doBusinessOperationXyz();
(2) new Verifications() {{ Database.persist(data); }};
(4) new Verifications() {{ email.send(); times = 1; }};
}
#Test(expected = EmailException.class)
public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
{
new Expectations() {{
(3) email.addTo((String) withNotNull()); result = new EmailException();
}};
EntityX data = new EntityX(5, "abc", "someone#somewhere.com");
new MyBusinessService(data).doBusinessOperationXyz();
}
}
Above is the class under test and below is a the test which specifically tests (3) part of the above code. I think it is similar to what you are trying to do. Check it out please.
#Test(expected = EmailException.class)
public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
{
new MockUp<Email>() {
#Mock
(3) Email addTo(String email) throws EmailException
{
assertNotNull(email);
throw new EmailException();
}
};
new MyBusinessService(data).doBusinessOperationXyz();
}
}
If you want to stick to jmock, it is fine. But then you need to give us more info about Desktop class and its browse method so that we can think about what we can do in jmock world.

Throwing and logging Exceptions, a better way

Ultimately, i'd like to
if (badThingsHappen) {
log the issue
throw exception with description
}
The obvious redundancy here is that often exception description and the message to be logged is (often) the same.
This looks needlessly verbose
if (badThingsHappen) {
logger.error("oh no! not again!");
throw new AppException("oh no! not again!");
}
Declaring temporary String feels wrong
if (badThingsHappen) {
String m = "oh no! not again!";
logger.error(m);
throw new AppException(m);
}
Is it ok to have Exception's constructor handle the logging? Is there a better (cleaner) way?
You could use a utility method:
public class AppException extends Exception {
public static AppException logAndThrow(Logger logger, String message) throws AppException {
AppException e = new AppException(message);
// log the stack trace as well
logger.error(message, e);
throw e;
}
}
and the use it:
if (badThingsHappen) {
AppException.logAndThrow(logger, "oh no! not again!");
}
I usually prefer to log exceptions when I catch them, rather then when I throw them.
This cleans up the logs quite a bit more, and also lets the "client" code handle the exception and information output much more precisely, since the information you want to associate with the exception when logging can be dependent of context.
If you do want to log as soon as it happens, I would build the exception and log it before throwing, something like:
if(badthingshappen){
Exception e = new Exception("holy $%##");
logger.log(e);
throw e;
}
A bit verbose yes... but this is java.
Typically when working with Exceptions and logging requirements I include logging support in the Exceptions.
Exceptions typically inherit from a Base Exception class in our project and it has hooks for logging log4j or other logging utilities.
class Problem extends java.lang.Exception {
private boolean debug=false;
public Problem(String message) {
if(debug) {
logging.exception(message);
/* Maybe a stack trace? */
}
}
}
I just wrote an error-logging method myself, today (this is used to log errors if they occur in a listener method, so it's also logging the method in which the error occurred and the object in which the listener is implemented to help tracking):
protected void listenerError(String listenerMethodName, Object listener,
RuntimeException e) {
logger.error("Exception while calling " + listenerMethodName
+ " on object " + listener, e);
throw e;
}
I wrote it in the class in question (or the base class, to be exact), because you probably want to use the logger in that class (and all subclasses). Another option would be to create a utility method in a utility class (I would not write an Exception class for it), and provide the logger as parameter:
class ExceptionUtil {
public static error(Exception e, Logger logger) {
logger.error(e);
throw e;
}
}
You can, of course, provide the method and object as params for this method (or an overloaded version of it), as necessary.

Mocking MessageDigest.getInstance() to throw an exception

I got the following method:
private MessageDigest getMessageDigest() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new Error(e);
}
}
To get 100% code coverage I need to get into the catch block. But I am absolutely not sure how I can do that. Is there some mocking framework that could help me in this case? If so - how? Or is there even another way without having to catch an exception?
The getInstance method on MessageDigest looks like a static method. Static methods cannot be mocked. I agree with ratchet that you should not aim for 100 % code coverage but focus on testing the areas with complex code instead.
I'd write this as:
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw (AssertionError)new AssertionError("unreachable").initCause(e);
}
And declare that because the catch block is unreachable, it doesn't need to be tested.
honestly in this case you don't need to cover that code it's non reachable boilerplate to ensure you don't have to worry about checked exceptions in the user code (most of the time 98% coverage is sufficient if you can explain why the 2 percent got missed)
Just to have a follow-up on this question, it can be done with PowerMock.
As an extract, this is my working code:
#RunWith(PowerMockRunner.class)
#PrepareForTest({MyClass.class, MessageDigest.class})
public class MyClassTest {
private MyClass myClass = new MyClass();
#Mock private MessageDigest messageDigestMock;
#Test
public void shouldDoMethodCall() throws Exception {
setupMessageDigest();
String value = myClass.myMethodCall();
// I use FestAssert here, you can use any framework you like, but you get
// the general idea
Assertions.assertThat(value).isEqualToIgnoringCase("hashed_value");
}
public void setupMessageDigest() throws Exception {
PowerMockito.mockStatic(MessageDigest.class);
when(MessageDigest.getInstance("SHA1")).thenReturn(messageDigestMock);
when(messageDigestMock.digest(Matchers.<byte[]>anyObject())).thenReturn("hashed_value".getBytes());
}
}
The class "MyClass" will simply do something like:
public class MyClass {
public String myMethodCall() {
return new String(MessageDigest.getInstance("SHA1").digest("someString".getBytes()));
}
}
In an additional test, you could write
when(MessageDigest.getInstance("SHA1")).thenThrow(new NoSuchAlgorithmException());
instead of my mentioned return, to get to your catch block.
Do note, however, that using PowerMock has some drawbacks. It will generally use more memory and more instatiation time, so your test will run longer. For this specific test, it won't make a big difference, but just as a head's up.
Your exception is unreachable because that exception will never be thrown. I suppose it's logical with something like Mockito to do something akin to:
doThrow(new NoSuchAlgorithmException()).when(MessageDigest.getInstance("MD5")); // this is psuedo code
But it still doesn't make much sense. You are better off writing your code like:
private static final MessageDigest MD5_DIGEST;
static {
try {
MD5_DIGEST = MessageDigest.getInstance("MD5");
///CLOVER:OFF
} catch (Exception e) {
// can't happen since MD5 is a known digest
}
///CLOVER:ON
}
public MessageDigest getMessageDigest() {
return MD5_DIGEST;
}
otherwise you'll need to modify your method to be testable:
public MessageDigest getMessageDigest(String digest) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(digest);
}
In my case, our pipeline needs 100% coverage, so I did the following:
define a static inner class with only one static method to return the instance of MessageDigest
define the method just as #TomAnderson did: in catch clause, throw an AssertionError("unreachable", e) to indicate that it is definitely impossible to reach here
ignore this static class in jacoco.gradle for jacocoTestReport and jacocoTestCoverageVerification tasks. To know how to exclude inner class, check my other post: How to ignore inner static classes in Jacoco when using Gradle
(which links to another post of how to do it in Maven, in case you use it)
I extract the method to a class, because Gradle does not have a consistent syntax to ignore members in a class. Check Filtering options of Jacoco and here
You can create a wrapper MessageDigest class:
#Component
public class MessageDigest {
public java.security.MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException {
return java.security.MessageDigest.getInstance(algorithm);
}
}

Categories

Resources