How to combine logging and unit-testing in java? - java

I've just started my very first toy-project in java and faced with misunderstanding of how it should be done. I'm using java.util.logging and JUnit4 library.
For example we have something like this:
public class SomeClass {
private static Logger log = Logger.getLogger(SomeClass.class.getName());
public static void SomeMethod() {
try{
...some code...
} catch(Exception e){
log.warning("Something bad happened");
}
}
And the unit-test will be:
#Test
public void SomeClassTest(){
SomeClass.SomeMethod();
}
But there will never be an exception, cause I've already handled it in method.
Should I generate new exception in catch-block? Or may be using junit combined with logging is not a good idea?

A method that does not throw an exception (and returns the expected value if any) is meant to work correctly from the perspective of a user.
So you should use try - catch and logging inside a method, when you can catch an exception and the method will still work correctly (do something else when this error happens for example but still return the expected result or perform the supposed operation).
In this case the unit test should check if the operation was performed correctly (if the object is in the expected state and the result value (in your case void) is correct
You should rethrow the exception (and usually not log it, but that depends) if the method cannot do what it is supposed to do when the exception occurs.
In this case the unit test should check if the operation was performed correctly (if the object is in the expected state and the result value (in your case void) is correct if there is no exception, and if there is an exception it should check if this exception was expected

If you want to test that the exception is thrown then you would have to re-throw, or not catch, the Exception.
Otherwise you can unit test that the class is in the correct state after the exception, i.e. that the exception was correctly handled.
I would say one other thing. Don't catch(Exception e), catch the specific exception you are expecting. Otherwise you will handle other, unexpected, exceptions in the same way and that is really quite dangerous.

You can simply rethrow the caught exception:
public class SomeClass {
private static Logger log = Logger.getLogger(SomeClass.class.getName());
public static void SomeMethod() {
try {
// your stuff
} catch (Exception e) {
log.warning("Something happened");
throw e;
}
}
}

Should I generate new exception in catch-block?
No. don't do that! you can test your existing code! when you only want to log the message but you don't want to handle it in the method that call someMethod() don't throw it!
using junit combined with logging is not a good idea?
both are good ideas and can be used together without problems.
Think about how you can test your method. i would not modify the code just that you can easily test it. because you WANTED to catch the exception and log for a reason.
try verifing what variables or objects are modified in your test-method

Related

Prevent exception from being logged in test

I have a SpringBoot test which asserts an exception is thrown for certain situations from the method tested. However the method tested catches and groups multiple errors, logs the details and (re) throws just one 'ServiceException' instead.
(log and rethrow the exact same exception would be an antipattern, this is not such case)
It is a service method which does much stuff and the user/client should not be bothered with all the details. Most of the issues would be irrelevant and there's nothing to do except maybe "try again later".
The test works correctly (passes when the exception is thrown) but I also see the original stacktrace logged (as it is supposed to when in production). However when doing tests, it is undesired to see this error show in logs as if it would be a real error. (Though could be a case for a test which is done poorly)
So the question is, how can I suppress the error from being logged just for this one test case?
(Preventing the logging to happen for all tests is not a solution. Exception would be needed just for a specific test case)
Example of the method to test:
public boolean isThisParameterGoodToUse(Object parameter) throws ServiceException {
try {
boolean allWasOk = true;
// Do stuff that may throw exceptions regardless of the parameter
return allWasOk;
} catch (IOException | HttpException | SomeException | YetAnotherException e) {
String msg = "There was a problem you can do nothing about, except maybe 'try again later'.";
this.log.error(msg, e); // Relevent for system monitors, nothing for the client
throw new ServiceException(msg);
}
}
And then the test would look something like this (Class is annotated with '#SpringBootTest' and it uses 'Jupiter-api'):
#Test
public void isThisParameterGoodToUse() {
assertThrows(ServiceException.class,
() -> this.injectedService.isThisParameterGoodToUse("This is not a good parameter at all!"));
}
And when I run the test, I get error message to log, e.g.:
com.myProd.services.SomeException: There was a problem you can do nothing about, except maybe 'try again later'.
at ... <lots of stackTrace> ...
If logging should be suppressed for a single test-class you can use
#SpringBootTest(properties = "logging.level.path.to.service.MyService=OFF")
If logging should be suppressed in all your tests then add this to your application.properties
test/resources/application.properties
logging.level.path.to.service.MyService=OFF
UPDATE
Suppress logging for a single test could be done by nesting your test in a separate class
#SpringBootTest
class DemoServiceTest {
#Autowired DemoService service;
#Test
void testWithErrorLogging() {
// ...
}
#Nested
#SpringBootTest(properties = {"logging.level.com.example.demo.DemoService=OFF"})
class IgnoreExceptionTests{
#Test
void isThisParameterGoodToUseWithOutError() {
Assertions.assertThrows(
ServiceException.class,
() -> {
service.isThisParameterGoodToUse("blabala");
}
);
}
}
}
Don't suppress the exception in logs, even in test.
Seeing exceptions thrown in tests is a good thing, since it means that your test covers a case in which they would be thrown.
The most desirable thing would be to validate that the exception along with the right message was thrown properly too (since you wouldn't want to mock the logger or spy on it or anything).
#Test
void isThisParameterGoodToUse() {
assertThrows(ServiceException.class,
() -> this.injectedService.isThisParameterGoodToUse("This is not a good parameter at all!"),
"There was a problem you can do nothing about, except maybe 'try again later'.");
}

How am I supposed to let an Unchecked Exception bubble up and log?

So quoting from this page, which is titled: Exception-Handling Antipatterns Blog and seems to be written (or at least to be approved) by Oracle..
An unchecked exception probably shouldn't be retried, and the correct response is usually to do nothing, and let it bubble up out of your method and through the execution stack. This is why it doesn't need to be declared in a throws clause. Eventually, at a high level of execution, the exception should probably be logged.
I am not sure if I understand this. How can I log an unchecked exception? If I have something like:
public static void main(String args) {
foo();
// How do I know what to log here? The method I am calling
// is not throwing an Exception.
// Do I just blindly catch(Exception ex)?
}
static void foo() {
bar();
}
static void bar() {
baz();
}
static void baz() {
// I will do nothing as Oracle suggests and let this exception bubble up.. I wonder who is going to catch it and how this is going to be logged though!
throw new NullPointerException();
}
Can you help me understand what Oracle is suggesting here? I do not see any direct (or clear) way to catch runtime exceptions (I do not understand why it is not just called unchecked exceptions..) in higher levels and I am not sure how this suggested practice is useful. To me it would make more sense if it were talking about checked exceptions. Something like..
If a checked exception is thrown in a method that is not reasonable to be re-tried, the correct response is to let it bubble up and log..
You can also register a global ExceptionHandler that will handle the Exceptions that were not caught by your code:
Thread.setDefaultUncaughtExceptionHandler
This exception handle could then log whatever occured.
First of all, this is a general advice and it depends on the context. The idea behind it is that when a runtime exception occurs (ex. NullPointerException), the system is usually in an indeterministic state, meaning the rest of the code is not be guaranteed to execute as expected, so it's better to stop everything.
In most cases, your code will run in a separate thread and the exception will only stop the current thread, while the rest of the program keeps running.
This is not the case in your example, because everything is executed in a single thread, so the uncaught exception will effectively stop the whole program. In this scenario you might want to catch the exception and handle it.
public static void main(String args) {
try {
foo();
catch(Throwable t) {
t.printStackTrace(); // log exception
// handle the failure
}
}
You can also catch the exception earlier on, log and rethrow it further.
static void bar() {
try {
baz();
catch (Throwable t) { // catch
t.printStackTrace(); // log
throw t; // rethrow further
}
}
Edit: catch Throwable instead of Exception, will also catch Error
Note: Catching throwable is usually a bad idea, and should only be done with a specific purpose, not in general case. See #RC.'s comment.
As I understand it the documentation is suggesting that you have a generic handler at a high level of your code that logs such 'unexpected' (unrecoverable?) exceptions just as the comments in your main method suggest. So it might look something like this
public static void main(String args) {
try {
foo();
}
catch (ArithmeticException aex) { //if it's arithmetic log differently
log("arith issue! "+aex.getMessage());
}
catch (Exception ex) { //Otherwise do the best we can
log("unknown issue! "+ex.getMessage())
}
}
So there is still no path to recovery but at least before the process ends you get a chance to log the issue. You can also use the methods of Exception (or throwable) to get the stack trace and first causal exceptions in many case - so there is is a lot of extra useful information that might be logged.
There is a very straightforward way to catch unchecked exceptions, since they are all subclasses of RuntimeException or Error:
public static void main(String[] args) {
try {
// your code
} catch (RuntimeException | Error e) {
// handle uncaught exceptions, e.g.
e.printStackTrace();
}
}
How do I know what to log here? The method I am calling is not throwing an Exception.
As Joshua Bloch recommends in the Effective Java
Use the Javadoc #throws tag to document each unchecked exception that
a method can throw, but do not use the throws keyword to include
unchecked exceptions in the method declaration
And if you are using method wrapping in multilayered app i can recommend use exception translation:
Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction
See Effective Java item 61
So i think for your example actually you should use something like:
try {
bar();
} catch(NullPointerException e) {
throw new HigherLevelException(...);
}
The most important guideline regarding exceptions is that a method that couldn't sucessfully complete its task should throw an exception.
Only if you can guarantee successful completion of your method's task, you should catch an exception inside your method (without re-throwing this or another exception). From my experience that's only true in very specific situations, e.g. if you have an alternative way to try if some first attempt fails, or if you really really understand all possible causes of this specific Exception class that you are about to catch.
Speaking about RuntimeExceptions, there are so many different types of RuntimeException that you can hardly justify an assertion like "When such an exception arises in my code or a method called from inside my code, that won't affect the outcome of my method - I can continue just as if nothing happened." So, you should signal to your caller that you failed to fulfill your task, and the clearest way to do that is to let the exception ripple through, without try/catch block or throws declaration, just relying on Java's default behaviour.
In my opinion, the same reasoning applies to nearly all kinds of exceptions, not only RuntimeExceptions.
The difference with checked exceptions is that you have to declare them in the throws clause of your method. Then you have two choices: list the exception in the throws clause of your method (and all parent methods as well!) or catch the exception, wrap it in a new RuntimeException(ex), and throw that from your method.
With e.g. a typical GUI application, your users will be grateful if a problem in one menu function won't crash the whole application - probably other menu items might still work as expected. So, top-level commands or menu items are typically the places where to catch exceptions, tell the user something like "Oops!", log the exception to some file for later inspection, and allow the user to continue with another action.
In your main/foo/bar/baz application, I don't see a place where continuing after an exception makes sense. So the whole program should be aborted (which happens automatically in your case). If you want some error logging to a file, then establish an uncaught exception handler or wrap the body of main() in a try / catch(Throwable t) block. You'll probably want every exception logged, whatever type it is, so catch them all, and that's why I'm suggesting Throwable.
public static void main(String[] args) {
try {
foo();
}
catch(NullPointerException e){
System.out.println("NullPointerException in main.");
}
}
static void foo() {
bar();
}
static void bar() {
baz();
}
static void baz() {
// I will do nothing as Oracle suggests and let this exception bubble up.. I wonder who is going to catch it and how this is going to be logged though!
throw new NullPointerException();
}
OUTPUT :
NullPointerException in main.
Basically the error is expected at a higher level, so there is no need to catch it on the baz() method level. If I understood correctly.
You can catch them just like any other exception with try-catch block. But the benefit is that you don't have to.
Use cases can vary. To my mind, the most popular is when it doesn't make sense to catch the exception right in that place or the appropriate handling should be implemented several levels (in terms of methods) higher than the method, calling the one throwing the exception (sorry, if that is not clear enough).
For example, the typical web application layout in java is as follows: you have a layer of controllers, a layer of services and a layer of dao. First one is responsible for dispatching requests, the second one is for managing business logic and the last one makes actual calls to db. So here for example it often doesn't make much sense to catch the exception in service layer if something goes wrong on the dao level. Here unchecked exceptions can be used. You log an exception and throw an unchecked exception so it could be handled some levels above for a user to get valuable feedback of work of the application.
If in this case you throw a checked exception you will have to rethrow it every level above just to bubble up it to the place of the actual handling. So here the unchecked exception is better to use in order not to copy and paste all that ugly try-catch block, rethrowing an exception and add the throws clause to the method.

EasyMock test an exception is thrown in the middle of code

I am writing test using EasyMock and there is a piece of source code like this:
public void doSomething(){
try
{
// Do something
}
catch (RejectedExecutionException ex)
{
// just add some metrics here, no big action
}
}
i am writing test for the case throwing RejectedExecutionException, but exception is not thrown finally, which means i can't use ExpectedException. So how should i test this exception is thrown once with EasyMock?
I do not think that you have a clean way to do this with any mockup framework. I however can suggest you the following solutions.
Solution 1
Modify you code of doSomething() as following:
public void doSomething(){
try
{
doSomethingImpl(); // throws RejectedExecutionException
}
catch (RejectedExecutionException ex)
{
// just add some metrics here, no big action
}
}.
Now implement test for both doSomethingImpl() that should throw exception and doSomething() that should not with the same input data and state.
Solution 2
You catch code does something, doesn't it? For example calls log.error(). You can verify that specific call indeed happened and happened only once. I do not remember the specific syntax to do this with EasyMock, but with Mockito it is very simple: use Mockito.verify().
Solution 3
You can use PowerMock to check that constructor of you exception was called. It is not very clean because theoretically you can create instance of exception but do not throw it, but it is better than nothing.
Probably you can even combine these solutions. However I believe that the first is the best one.
The Do something part calls a mock throwing the exception?
If yes, just do expect(mock.methodCalled()).andThrow(new RejectedExecutionException());
And then EasyMock.verify(mock) at the end of your test. This will make sure methodCalled was called once and only once.

Try catch in a JUnit test

I'm writing unit tests for an application that already exists for a long time. Some of the methods I need to test are build like this:
public void someMethod() throws Exception {
//do something
}
If I want to test these methods I have to write something like this in my unit test:
#Test
public void someTest() {
try {
someMethod();
}
catch (Exception e) {
e.printStackTrace();
}
}
Is it a good practice to do this? Or is there an other way to test these methods?
I did some research on the internet and I found a few solutions with the #Rule annotation and #Test(expected=Exception.class), but that's not working (Eclipse keeps showing the someMethod() line in the test as wrong).
I don't know if these are good solutions, because I'm pretty new to the whole unit testing story.
If someone who knows a lot about this could help me out, I would be really thankful.
Since Exception is a checked exception, you either:
Have to catch the exception in a try...catch statement, or
Declare the exception to be thrown in the method itself.
What you have up there works fine, but my personal preference is to declare the exception to be thrown. This way, if an exception I'm not expecting is thrown during the run of the test, the test will fail.
#Test
public void someTest() throws Exception {
// dodgy code here
}
If we need to see if a specific exception is thrown, then you have the option of using #Rule or adding the value to the #Test annotation directly.
#Test(expected = FileNotFoundException.class)
public void someTest() throws Exception {
// dodgy code here
}
In JUnit 5, you can leverage Assertions.assertThrows to accomplish the same thing. I'm less familiar with this overall since it's not yet GA at the time of editing, but it appears to accept an Executable coming from JUnit 5.
#Test
public void someTest() {
assertThrows(FileNotFoundException.class, () ->
{ dodgyService.breakableMethod() };
}
#Test
public void someTest() {
try {
someMethod();
}
catch (Exception e) {
Assert.fail("Exception " + e);
}
}
Is what you can do, if the exception should not occur. An alternative would be to throw the exception in the signature like this:
#Test
public void someTest() throws Exception {
someMethod();
}
The difference is, that in one case the test will fail with an assertion exception and in the other case it will fail because the test crashed. (like somewhere in your code you get a NPE and the test will because of that)
The reason you have to do this, is because Exception is a checked exception. See Checked versus unchecked exception
The #Test(expected=Exception.class) is for tests, that want to test that the exception will be thrown.
#Test(expected=ArrayIndexOutOfBounds.class)
public void testIndex() {
int[] array = new int[0];
int var = array[0]; //exception will be thrown here, but test will be green, because we expect this exception
}
Do not catch your application's exception in your test code. Instead, declare it to be thrown upwards.
Because, when JUnit's TestRunner finds an exception thrown, it will automatically log it as an error for the testcase.
Only if you testcase expects that the method should thrown an Exception you should use #Test(expected=Exception.class) or catch the exception.
In other cases, just throw it upwards with,
public void someTest() throws Exception {
You can add exception in test method signature. Then, if you are testing whether exception is thrown, you have to use #Test(expected=Exception.class). In the test cases where exception has not to be thrown, test will pass successfully.
#Test
public void testCaseWhereExceptionWontBeThrown() throws Exception {
someMethod(); //Test pass
}
#Test(expected = Exception.class)
public void testCaseWhereExceptionWillBeThrown() throws Exception {
someMethod(); //Test pass
}
There are two main rules on how to process exceptions at Junit testers:
If the exception was originated into the tested code:
If it was expected, declare it in the expected attribute of the Test annotation. Or, if further checks should be done on the exception object itself, catch it and ignore it. (In this case, there must be also a call to Assert.fail at the end of the try block, to indicate that the expected exception was not produced).
If it was not expected, catch it and execute Assert.fail. (A previous call to Exception.printStackTrace is also useful).
If the exception was not originated into the tested code or it is not interesting to the test (for example, most of the IOExceptions are produced at network level, before the test could even be completed), rethrow it at the throws clause.
Why you should expect an exception in the tester? Remind: You should code one test method for every possible result on the tested code (in order to achieve a high code coverage): In your case, one method that must return successfully, and at least another one that must produce an Exception.
Three points about JUnit:
Tests should be precise, they should pass or fail unambiguously based solely on how the test inputs are set up.
Tests should have failures reported back into the framework.
Tests should not rely on having their output read.
Your example fails on all three counts. If an exception gets thrown or not, the test still passes. If an exception is thrown JUnit never finds out about it and can't include it in the test results. The only way to know something went wrong is to read what the test writes to stdout, which makes errors too easy to ignore. This is not a useful way to write tests.
JUnit was designed to make doing the right thing easy and to give developers useful feedback. If an exception gets thrown from a test method, it gets caught by the framework. If the test was annotated with an exception indicating that exception is expected, then the framework marks the test as passing. Otherwise the framework fails the test and records the stacktrace for reporting. The framework reports what assertions fail and what unexpected exceptions occurred so that everybody knows if the tests worked or not.
If you expect a test to succeed without throwing an exception, then if anything in the test can throw a checked exception, add throws Exception to the test method signature. Adding the throws to the signature doesn't say the method has to throw anything, it just lets any exceptions that happen to occur get thrown so that the test framework can catch them.
The only instance where you would actually catch the exception in the test is where you want to test assertions about the exception; for instance, you could test that the message on the exception is what you expect, or if the exception has a cause set on it. In that case you would add Assert.fail() at the end of the try-block so that not having an exception thrown will cause the test to fail.
It isn’t having a try-catch block that is so bad, it’s the absence of anything that will cause the test to fail that is bad.
When you write a test at first, make it fail. That way you prove to yourself that you know what the test is doing, and you confirm that, when there is a failure, you will be made aware of it.
What kind of exception is it? Is it
an exception from doing something like using streams that won't happen in your unit test or
an exception that can happen because of some kind of bad input?
If it's 1. I would just put it at the method signature level because a try-catch is serving no real purpose other than ceremony.
#Test
public void testFoo() throws Exception {
// ...
}
If it's 2. it becomes a little more complicated. You need to ask yourself what should be happening if the Exception is thrown. Should the test fail? Is it expected? Is it irrelevant? Examples below of how to handle all of these. BEWARE: I only used Exception because you did. I hope it really isn't though because if it's possible for some other exception to be thrown other than the expected then these will be very wonky. If possible don't use Exception, use something more specific (in the junit and code).
// The below code assumes you've imported the org.junit.Assert class.
#Test
public void thisShouldFailIfExceptionCaught() {
//Given...
try {
// When...
} catch (Exception e) {
Assert.fail();
}
// Then...
}
#Test
public void thisShouldPassOnlyIfTheExceptionIsCaught() {
//Given...
try {
// When...
Assert.fail();
} catch (Exception expected) {}
// No "then" needed, the fact that it didn't fail is enough.
}
#Test
public void irrelevantExceptionThatCouldBeThrown() {
//Given...
try {
// When...
} catch (Exception e) {}
// Then...
}

Should jUnit test cases handle default exceptions in a throws declaration or in a try catch block

If I write test cases for a function that throws a bunch of exceptions should I add a throws declaration for these exceptions in my test method or should I catch each individual exception. What is the correct way of going about it? I believe try-catch is a better way but in the catch block should I print the stacktrace?
For example, I have a method getGroups(String name) that throws AuthenticationException. If I write a test case to check if an IllegalArgumentException is being thrown when the name parameter is null, how do I handle the AuthenticationException? Do I add it to throws part of my method or should I enclose the exception in a try-catch block.
#Test
public void testGetGroupsWithNull() throws AuthenticationException {
thrown.expect(IllegalArgumentException.class);
getGroups(null);
}
In the above test case I just added a throws AuthenticationException, but I would like to know if it is better to enclose the exception in a try-catch block and what shoudld I do after catching the exception. I could print the stack trace.
I am handling the unexpected exception AuthenticationExceptionby not placing it in the 'throws' clause but in a try/catch block.
#Test
public void testGetGroupsWithNull() {
thrown.expect(IllegalArgumentException.class);
try {
getGroups(null);
} catch(AuthenticationExcption e) {
Assert.fail("Authentication Exception");
}
}
JUnit has a great article here: https://github.com/junit-team/junit/wiki/Exception-testing on this very subject.
You can do:
#Test(expected= IndexOutOfBoundsException.class)
public void empty() {
new ArrayList<Object>().get(0);
}
or:
#Test
public void testExceptionMessage() {
try {
new ArrayList<Object>().get(0);
fail("Expected an IndexOutOfBoundsException to be thrown");
} catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
}
}
If a JUnit test throws an unexpected exception, it fails. That is the behaviour that you want. So there's no point in EVER using a try/catch block. If you're expecting an exception, use an ExpectedException rule (which you obviously know about, from your code snippet). But whether you're expecting one or not, don't use try/catch.
This means that if your exception is a checked exception, you need a throws clause. In fact, you'll often need a throws clause on your test method, even when you're NOT expecting the exception to be thrown, just because your test calls a method that can SOMETIMES throw a checked exception. I have got into the habit of writing throws Exception on every single test method. There is no reason not to; and it's just one less thing to worry about.
The annotation is more communicative.
It signals what the test expects to happen without forcing the reader to read the code.
Any single test should only expect a single exception to be thrown, because each test should be testing a single behavior. A single behavior can only throw one exception.
If any other exception is thrown it's a test failure. The test method signature must reflect any possible checked exceptions, of course, as would real code calling that same method.
Using the rule of writing as little code as possible to solve the problem, your first code snippet wins. So yes, put the AuthenticationException into your test method's throws clause. It is more succinct and readable.
I've just looking for the same question since I'm dealing with your topic and I found a good explanation for unit test best practices. A little extraction from the article can help you.
It is unnecessary to write your own catch blocks that exist only to fail a test because the JUnit framework takes care of the situation for you. For example, suppose you are writing unit tests for the following method:
final class Foo {
int foo(int i) throws IOException;
}
Here we have a method that accepts an integer and returns an integer and throws an IOException if it encounters an error. Here is the wrong way to write a unit test that confirms that the method returns three when passed seven:
// Don't do this - it's not necessary to write the try/catch!
#Test
public void foo_seven()
{
try
{
assertEquals(3, new Foo().foo(7));
}
catch (final IOException e)
{
fail();
}
}
The method under test specifies that it can throw IOException, which is a checked exception. Therefore, the unit test won't compile unless you catch the exception or declare that the test method can propagate the exception. The second alternative is preferred because it results in shorter and more focused tests:
// Do this instead
#Test
public void foo_seven() throws Exception
{
assertEquals(3, new Foo().foo(7));
}
We declare that the test method throws Exception rather than throws IOException. The JUnit framework will make sure that this test fails if any exception occurs during the invocation of the method under test - there's no need to write your own exception handling.
You can find more about JUnit best practices like above in this article:
http://www.kyleblaney.com/junit-best-practices/
Hope to help.

Categories

Resources