I am starting out in Java using DrJava. I am following TDD for learning. I created a method which is suppose to validate some data and on invalid data, the method is suppose to throw exception.
It is throwing exception as expected. But I am not sure, how to write a unit test to expect for exception.
In .net we have ExpectedException(typeof(exception)). Can someone point me to what is the equivalent in DrJava?
Thanks
If you are using JUnit, you can do
#Test(expected = ExpectedException.class)
public void testMethod() {
...
}
Have a look at the API for more details.
If you simply want to test for the fact that a particular exception type was thrown somewhere within your test method, then the already shown #Test(expected = MyExpectedException.class) is fine.
For more advanced testing of exceptions, you can use an #Rule, in order to further refine where you expect that exception to be thrown, or to add further testing about the exception object that was thrown (i.e., the message string equals some expected value or contains some expected value:
class MyTest {
#Rule ExpectedException expected = ExpectedException.none();
// above says that for the majority of tests, you *don't* expect an exception
#Test
public testSomeMethod() {
myInstance.doSomePreparationStuff();
...
// all exceptions thrown up to this point will cause the test to fail
expected.expect(MyExpectedClass.class);
// above changes the expectation from default of no-exception to the provided exception
expected.expectMessage("some expected value as substring of the exception's message");
// furthermore, the message must contain the provided text
myInstance.doMethodThatThrowsException();
// if test exits without meeting the above expectations, then the test will fail with the appropriate message
}
}
Related
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'.");
}
I have a project where I have tests where I deliberately cause a problem and then verify the code responds the way I want it. For this I want to be sure the exceptions not only are the right class but they must also carry the right message.
So in one of my existing (junit 4) tests I have something similar to this:
public class MyTests {
#Rule
public final ExpectedException expectedEx = ExpectedException.none();
#Test
public void testLoadingResourcesTheBadWay(){
expectedEx.expect(MyCustomException.class);
expectedEx.expectMessage(allOf(startsWith("Unable to load "), endsWith(" resources.")));
doStuffThatShouldFail();
}
}
I'm currently looking into fully migrating to junit 5 which no longer supports the #Rule and now has the assertThrows that seems to replace this.
What I have not been able to figure out how to write a test that not only checks the exception(class) that is thrown but also the message attached to that exception.
What is the proper way to write such a test in Junit 5?
Since Assertions.assertThrows returns instance of your exception you can invoke getMessage on the returned instance and make assertions on this message :
Executable executable = () -> sut.method(); //prepare Executable with invocation of the method on your system under test
Exception exception = Assertions.assertThrows(MyCustomException.class, executable); // you can even assign it to MyCustomException type variable
assertEquals(exception.getMessage(), "exception message"); //make assertions here
Thanks to #michalk and one of my colleagues this works:
Exception expectedEx = assertThrows(MyCustomException.class, () ->
doStuffThatShouldFail()
);
assertTrue(expectedEx.getMessage().startsWith("Unable to load "));
assertTrue(expectedEx.getMessage().endsWith(" resources."));
I have method that throws an exception in special circumstances. I would like to write a test case that will check behaviour when exception is not thrown.
I cannot find this in docs or examples. Please help.
E.g.:
when(validator.validate(any(ValidationData.class))).thenThrow(new ValidationException());
But I would like to test that exception is not thrown at all:
class Validator {
void validate(ValidationData dataToValidate) throws Exception {
}
}
e.g. I need something like:
when(doSomething()).thenNotThrowException
or
when(doSomething()).thenDoNothing
By default, Mockito's mock does nothing for void methods, so you don't need to write anything.
If you want to do this explicitly try this:
doNothing().when( validator ).validate( any() );
To test the case where no exceptions is thrown, you actually need to do even less:
Do not program the thenThrow at all
In your test case, expect the test method to run normally and complete without exceptions (in Junit, don't have any expected attribute for #Test)
If the test is executed without errors, then your test passed.
If you just want to test that there are no exceptions in a test function, you should use this annotation: #Test(expected = Test.None.class)
Example:
#Test(expected = Test.None.class)
public void testFunction() {
// some code
}
This question already has answers here:
Why won't this Expect Exception Junit Test Work?
(2 answers)
Closed 6 years ago.
I'm having an issue with a test case.
The method being tested has a try / catch that catches a MalformedURLException but during testing I get a failure due to a Junit AssertionError that expects a MalformedURLException. But I can't find out what it is actually throwing! Here is my code (created as a MWE in eclipse).
My method that I want to test
public void throwMalFormedURLException(){
String s = new String("www.google.com");
try{
url = new URL(s);
}catch (Exception e){
e.printStackTrace();
e.getClass();
e.getCause();
}
}
the test method
#Test (expected = MalformedURLException.class)
public void testThrowMalFormedURLException() {
MWE testClass = new MWE();
testClass.throwMalFormedURLException();
System.out.println("End of test");
}
This is the output in the console
End of test error details are: java.net.MalformedURLException: no protocol: www.google.com
at java.net.URL.(URL.java:593)
at java.net.URL.(URL.java:490)
at java.net.URL.(URL.java:439)
at MWE.throwMalFormedURLException(MWE.java:12)
at testMWE.testThrowMalFormedURLException(testMWE.java:12)
In the Junit console it says :
java.lang.AssertionError: Expected exception: Java.net.MalformedURLException
But the Junit is reporting failure, even though the console is telling me I've got a MalformedURLException.
What am I doing wrong with this test ?
Thanks for your thoughts.
David
You are catching the exception and therefore it is not being thrown.
If your intent is to test that you are capturing the exception and relaying it back to the 'user' properly, you should create tests for that specifically. You probably don't want UI elements in your unit tests* so this is a place where abstraction and DI have a lot of value. One simple approach is to create a mock object that will listen for the error message and mark a flag when the message is received. Your unit test trigger the error and then pass if the flag is set. You should also have a negative test or assert that the flag is not set prior to throwing the exception.
*Testing the UI is also a good idea but it can be a little slow. There are various tools for automating that. It generally falls in to a different phase of testing. You really want the unit tests to be really fast so that you can run them very frequently.
You have written production code that simply isn't testable. And more specifically: this code doesn't have any "programmatically" observable side effects in the first place.
Meaning: when you write "production code", the code within a method can do three things that could be observed:
Make method calls on objects that are fields of the class under test
Return some value
Throw an exception
For each of these options, you might be able to write testing code:
Using dependency injection, you can put a mocked object into your class under test. And then you can check that the expected methods are invoked on your mock object; with the parameter that you would expect.
You compare the result you get from calling that method with some expected value
You use expected to ensure that a specific exception was thrown
When you look at your code; you can see: it does none of that. It only operates on objects that are created within the method. It doesn't return any value. And, most importantly: it doesn't throw an exception!
Long story short: a caught exception isn't "leaving" the method. It is caught there, and the method ends normally. The fact that you print details about the caught exception doesn't change that.
So, the first thing you have to do: remove the whole try/catch from your production code!
And, if you want to have a more specific test, you can do something like:
#Test
public void testException() {
try {
new MWE().throwMalFormedURLException();
fail("should have thrown!");
} catch ( MalFormedURLException me ) {
assertThat(me.getMessage(), containsString("whatever"));
}
The above:
fails when no exception is thrown
fails when any other exception than MalFormedURLException is thrown
allows you to check further properties of the thrown exception
This is a valid test failure. The test asserts that calling throwMalFormedURLException() will throw MalformedURLException, but since you're catching the exception, it doesn't throw it, so the test fails.
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...
}