Validate concrete exception details in JUnit - java

To validate exception message I can use ExpectedMessage object but what if I want to validate concreate exception and it's details? For example I have exception class TerribleWindException which has some additional methods like getWindSpeed() and in junit test method I want to check the getWindSpeed() result. Look at this example:
// pseudo implementation
public class TerribleWindException extends Exception {
private Integer windSpeed = 0;
public TerribleWindException(final Integer windSpeed) {
this.windSpeed = windSpeed;
}
}
public class WalkService {
private Integer windSpeed = 0;
public WalkService(final Integer windSpeed) {
this.windSpeed = windSpeed;
}
public void goForAWalk() throws TerribleWindException {
if (windSpeed>10) {
throw new TerribleWindException(windSpeed);
}
}
}
// test
public class WalkServiceTest {
#Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
WalkService ws = new WalkService(100);
goForAWalk(); // this will throw TerribleWindException. The only way to check it's exception details is to use try {} catch() {} ?
}
}
The only way to check exception details is to use try {} catch() {} ?

You could use JUnit's ExpectedException rule together with Hamcrest matchers.
public class WalkServiceTest {
#Rule
public final ExpectedException thrown = ExpectedException.none();
#Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
WalkService ws = new WalkService(100);
thrown.expect(TerribleWindException.class);
thrown.expect(Matchers.hasProperty("windSpeed", Matchers.equalTo("expected speed")));
ws.goForAWalk(); // this will throw TerribleWindException. The only way to check it's exception details is to use try {} catch() {} ?
}
}
If you're using Java 8 then you can use the Vallado library together with Hamcrest.
public class WalkServiceTest {
#Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
WalkService ws = new WalkService(100);
when(() -> ws.goForAWalk())
.thenA(TerribleWindException.class)
.that(hasProperty("windSpeed", equalTo("expected speed")))
.isThrown();
}
}

For such case i suggest you to use catch-exception library from Google, code sample (from their homepage):
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionBdd.*;
// given: an empty list
List myList = new ArrayList();
// when: we try to get the first element of the list
when(myList).get(1);
// then: we expect an IndexOutOfBoundsException
then(caughtException())
.isInstanceOf(IndexOutOfBoundsException.class)
.hasMessage("Index: 1, Size: 0")
.hasNoCause();
See also catch-exception AssertJ api if above example is not enough.

Yes. You can use an #Test(expected=Exception.class) annotation to test the class of the exception thrown, but to test the details you will need to catch and interrogate the exception yourself.
Don't forget to fail() if the exception isn't thrown. e.g.
try {
operation();
fail("Expected an exception");
}
catch (MyExpectedException e) {
// assertions...
}

If you use Java 8 there is actually a more flexible way to achieve what you want, which uses Lambdas.
The following steps are necessary:
Create a getter for the windSpeed-attribute of your Exception
Create a functional interface for a lambda which takes no parameters and returns nothing and which might throw an exception, like this (Runnable is not suitable, because it does not throw an exception and Callable has a return value):
#FunctionalInterface
public interface TestCommand {
public void exec() throws Exception;
}
Create your own assertion for exceptions, like this:
public <T extends Exception> void myAssertThrows(TestCommand testAction,
Predicate<T> p) {
try {
testAction.exec();
fail("must throw exception");
} catch (Exception exception) {
assertTrue(p.test((T) exception));
}
}
Use it in a test:
#Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException(){
WalkService ws = new WalkService(10);
myAssertThrows(() -> {
ws.goForAWalk();
}, (TerribleWindException e) -> e.getWindSpeed() == 100);
}
So how does it work? ws.goForAWalk(); is packed into a lambda of type TestCommand so it is not immediately executed. It is the first parameter of myAssertThrows and executed in this method. The second parameter is a predicate on Exception, which is simply a lambda which takes one exception as parameter, returns a boolean and can be used to check properties of the exception, which is thrown. The assert method uses the well-known try-catch-style.
You could actually also use the assert method in Java 7, but the call would be less readable, because it would need anonymous classes.

This one makes more sense than my previous answer, but I am not editing my previous answer because it serves most of the use cases.
The ExpectedException rule allows you to specify, within your test, what exception you are expecting and even what the exception message is
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class CloudTest {
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void testExpectedException() {
exception.expect(CustomException.class);
exception.expectMessage(containsString('Invalid cloud type'));
new Cloud("Rainy","With thunder", "big");
}
}

You could use Junit's Rules Junit Rules, which includes an ExpectedException rule, which can be used to also test messages of exceptions.
EDIT:
If you implemented TerribleWindException the following way:
public class TerribleWindException extends Exception {
private Integer windSpeed = 0;
public TerribleWindException(final Integer windSpeed) {
// use Exception constructor, which passes a message
super("windSpeed: " + windSpeed);
this.windSpeed = windSpeed;
}
}
You could achieve what you want using this test code:
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
List<Object> list = new ArrayList<Object>();
thrown.expect(TerribleWindException .class);
thrown.expectMessage("windSpeed: 100");
WalkService ws = new WalkService(100);
ws.goForAWalk();
}

I often use a scenario like the following:
#Test
public void testGoForAWalkWhenSpeedIsToPowerfulShouldThrowTerribleWindException throws TerribleWindException {
WalkService ws = new WalkService(100);
try {
goForAWalk();
fail();
catch(TerribleWindException twe) {
}
}

No you can catch specific exception by using the following template
#Test(expected= TerribleWindException.class)

Related

Assert expected exception with specific criteria in custom fields

I want to verify that an expected exception meets certain criteria. Take this as a starting point:
class MyException extends RuntimeException {
int n;
public MyException(String message, int n) {
super(message);
this.n = n;
}
}
public class HowDoIDoThis {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void test1() {
thrown.expect(MyException.class);
throw new MyException("x", 10);
}
}
How do I assert, for example, that the thrown exception has n > 1 and message contains only lowercase letters? I was thinking of using thrown.expect(Matcher) but can't figure out how to get a Hamcrest matcher to check arbitrary fields of an object.
A consise and neat alternative way is to use AssertJ instead of the ExpectedException rule.
assertThatThrownBy(() -> {
throw new MyException("x", 10);
})
.matches(e -> e.getMessage().equals(e.getMessage().toLower()), "message is lowercase")
.matches(e -> ((CustomException) e).n > 10, "n > 10");
You can use TypeSafeMatcher where you can provide your MyException class, and then an IntPredicate to check the n value against a condition:
public class MyExceptionMatcher extends TypeSafeMatcher<MyException> {
private final IntPredicate predicate;
public MyExceptionMatcher(IntPredicate predicate) {
this.predicate = predicate;
}
#Override
protected boolean matchesSafely(MyException item) {
return predicate.test(item.n);
}
#Override
public void describeTo(Description description) {
description.appendText("my exception which matches predicate");
}
}
Then you can expect like so:
thrown.expect(new MyExceptionMatcher(i -> i > 1));
There's also FeatureMatcher in Hamcrest that is great for creating matchers for nested "features" of objects. So in your example, you could structure it using FeatureMatcher in the following way (this is the pattern I tend to follow when creating matchers for nested fields):
public final class MyExceptionMatchers {
public static Matcher<MyException> withNthat(Matcher<Integer> nMatcher) {
return new FeatureMatcher<MyException, Integer>(nMatcher, "n", "n") {
#Override
protected Integer featureValueOf(MyException actual) {
return actual.n;
}
}
};
}
And in your test:
import static x.y.z.MyExceptionMatchers.withNthat;
import static org.hamcrest.Matchers.greaterThan;
...
thrown.expect(withNThat(greaterThan(1)));
With this layout it's very easy to add more matchers for MyException and it feels like a more "canonical" approach to build composable matchers that allow you to construct the exact matcher you want for your test case.

How to write a junit to verify if an exception thrown by the method is caught?

I have below piece of code in my spring boot app, which validates email addresses
class EmailValidation {
public static void validate(List<String> s){
try {
for (String address : s) {
if (s == null || s.indexOf("#") < 0) {
throw new InvalidEmailAddressException("Email address is invalid ");
}
new InternetAddress(s);
}
} catch(AddressException e){
LOGGER.Error("Please validate email addresses");
}
catch(InvalidEmailAddressesException e){
LOGGER.error(e.getMessage());
}
}
class InvalidEmailAddressException extends Exception {
public InvalidEmailAddressException(String message) {
super(message)
}
}
}
I want to write a Junit test which will verify that that InvalidEmailAddressesException was thrown and CAUGHT. How can I do that in JUnit?
In general I agree with the comments that such a test is probably unnecessary.
However, if I wanted to test something like that I would test the two cases separately and that requires a small modification to your code.
Firstly I would construct a method that only throws the exception if there is one.
public static void checkAddresses(List<String> s) throws AddressException, InvalidEmailAddressException {
for (String address : s) {
if (s == null || s.indexOf("#") < 0) {
throw new InvalidEmailAddressException("Email address is invalid ");
}
new InternetAddress(s);
}
}
then I would use it in your code like that:
class EmailValidation {
public static void validate(List<String> s){
try {
checkAddresses(s); // a wrapper method that throws the expected exceptions
} catch(AddressException e){
LOGGER.Error("Please validate email addresses");
}
catch(InvalidEmailAddressesException e){
LOGGER.error(e.getMessage());
}
}
// add checkAddresses here or somewhere appropriately
class InvalidEmailAddressException extends Exception {
public InvalidEmailAddressException(String message) {
super(message)
}
}
}
Then, I would write separate tests for checkAddresses that tests both if an exception is expected or not and separate tests for validate, (possibly with the same input that was given to checkAddresses) that should pass if an exception isn't thrown.
Also, if you would like to verify your logs may be you could try something like that.
Indeed using java Exception for common cause is considered a bad practice, and as #Michael said, Exceptions must be exceptional, because
they break flow control
they are slow (more details here How slow are Java exceptions?)
they do not mix with functional paradigm (where Java is in part going to with the addition of lamda-expressions
However, creating a custom object for wrapping validation data is a good thing and InvalidEmailAddressException can be turned into CheckedEmail:
import java.util.List;
import java.util.stream.Collectors;
public class EmailValidator {
public List<CheckedEmail> validate(List<String> emailAddresses) {
return emailAddresses.stream().map(this::validate).collect(Collectors.toList());
}
public CheckedEmail validate(String emailAddress) {
String[] emailParts = emailAddress.toString().split( "#", 3 );
final boolean valid;
if ( emailParts.length != 2 ) {
valid = false;
} else {
// More validation can go here using one or more regex
valid = true;
}
return new CheckedEmail(emailAddress, valid);
}
public static final class CheckedEmail {
private final String emailAddress;
private final boolean valid;
private CheckedEmail(String emailAddress, boolean valid) {
this.emailAddress = emailAddress;
this.valid = valid;
}
public String getEmailAddress() {
return emailAddress;
}
public boolean isValid() {
return valid;
}
}
}
This in turn can be tested quite easily (and improved with a parameterized test):
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class EmailValidatorTest {
private final EmailValidator emailValidator = new EmailValidator();
#Test
public void invalid_email() {
EmailValidator.CheckedEmail checkedEmail = emailValidator.validate("missing.an.at.symbol");
assertThat(checkedEmail.isValid()).isFalse();
}
#Test
public void valid_email() {
EmailValidator.CheckedEmail checkedEmail = emailValidator.validate("at.symbol#present");
assertThat(checkedEmail.isValid()).isTrue();
}
#Test
public void multiple_email_addresses() {
List<String> emailAddresses = Arrays.asList("missing.an.at.symbol", "at.symbol#present");
List<EmailValidator.CheckedEmail> checkedEmails = emailValidator.validate(emailAddresses);
assertThat(checkedEmails)
.extracting(ce -> ce.getEmailAddress() + " " + ce.isValid())
.containsExactly(
"missing.an.at.symbol false",
"at.symbol#present true");
}
}
If somewhere the point is just to log this, then:
List<EmailValidator.CheckedEmail> checkedEmails = emailValidator.validate(emailAddresses);
checkedEmails.stream()
.filter(ce -> !ce.isValid())
.map(ce -> String.format("Email address [%s] is invalid", ce.getEmailAddress()))
.forEach(logger::error);
Hope this helps !
Don't approach testing that way. You should test only the specified behaviour of your code, not its implementation details.
If the method you are testing delegates to a method that throws a checked exception, and the method you are testing does not also declare that it throws that checked exception, the compiler will enforce that the method catches the exception. So in that case a unit test is unnecessary.
If the method you are testing delegates to a method that throws an unchecked exception, consult the specification of the method to determine whether it is acceptable for the method under test to also throw (propagate) that exception. If it is not acceptable for it to propagate the exception, then you should create a test case that causes the the method delegated to to throw that unchecked exception. If the method propagates the exception, the test case will fail. How to do that? That depends on the method being delegated to, but in most cases you will need to use Dependency Injection to supply a mock object that throws the exception.

How can I use JUnit's ExpectedException to check the state that's only on a child Exception?

I'm trying to refactor this old code that does not use ExpectedException so that it does use it:
try {
//...
fail();
} catch (UniformInterfaceException e) {
assertEquals(404, e.getResponse().getStatus());
assertEquals("Could not find facility for aliasScope = DOESNTEXIST", e.getResponse().getEntity(String.class));
}
And I can't figure out how to do this because I don't know how to check the value of e.getResponse().getStatus() or e.getResponse().getEntity(String.class) in an ExpectedException. I do see that ExpectedException has an expect method that takes a hamcrest Matcher. Maybe that's the key, but I'm not exactly sure how to use it.
How do I assert that the exception is in the state I want if that state only exists on the concrete exception?
The "best" way is a custom matcher like the ones described here: http://java.dzone.com/articles/testing-custom-exceptions
So you would want something like this:
import org.hamcrest.Description;
import org.junit.internal.matchers.TypeSafeMatcher;
public class UniformInterfaceExceptionMatcher extends TypeSafeMatcher<UniformInterfaceException> {
public static UniformInterfaceExceptionMatcher hasStatus(int status) {
return new UniformInterfaceExceptionMatcher(status);
}
private int actualStatus, expectedStatus;
private UniformInterfaceExceptionMatcher(int expectedStatus) {
this.expectedStatus = expectedStatus;
}
#Override
public boolean matchesSafely(final UniformInterfaceException exception) {
actualStatus = exception.getResponse().getStatus();
return expectedStatus == actualStatus;
}
#Override
public void describeTo(Description description) {
description.appendValue(actualStatus)
.appendText(" was found instead of ")
.appendValue(expectedStatus);
}
}
then in your Test code:
#Test
public void someMethodThatThrowsCustomException() {
expectedException.expect(UniformInterfaceException.class);
expectedException.expect(UniformInterfaceExceptionMatcher.hasStatus(404));
....
}

How to create own annotation for junit that will skip test if concrete exception was thrown during execution?

My application have several execution modes, and in 1 mode it is normal that some of my tests will throw a concrete exception. I need to annotate this methods with something like #SkipOnFail that will set method as skipped if exception was thrown.
thanks in advance!
#Edit(for my question to be more clear)
#Test(expected=ConcreteException.class)
does not work for me because i need my tests to pass even if ConcreteException.class was not thrown(expected tag in junit will mark my test as failed if this exception won't be thrown), and to be skipped otherwise. In all other cases it should work as always.
#Solution that worked for me(junit v4.7) thx to #axtavt
#Rule
public MethodRule skipRule = new MethodRule() {
public Statement apply(final Statement base, FrameworkMethod method, Object target) {
if(method.getAnnotation(SkipOnFail.class) == null) return base;
return new Statement() {
#Override
public void evaluate() throws Throwable {
try{
base.evaluate();
} catch (ConcreteException e) {
Assume.assumeTrue(false);
}
}
};
}
};
#Thx
I don't think that such a feature is available out of the box, but it should be pretty easy to implement with custom TestRule and Assume, something like this:
#Rule
public TestRule skipRule = new TestRule() {
public Statement apply(final Statement base, Description desc) {
if (desc.getAnnotation(SkipOnFail.class) == null) return base;
return new Statement() {
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (MyExceptoion ex) {
Assume.assumeTrue(false);
}
}
};
}
};
What about using JUnit Extensions?
The following example is taken from their Tutorial.
It provides aditional annotations for Prerequisites (#Prerequisite): Ignore tests based on conditions.
The required approach would be to check this during running tests. So you can simply add a #Prerequisite(requires="") annotation.
public class TestFillDatabase {
#Prerequisite(requires = "databaseIsAvailable")
#Test public void fillData() {
// ...
}
public boolean databaseIsAvailable() {
boolean isAvailable = ...;
return isAvailable;
}
}
public class TestFillDatabase {
#Prerequisite(requires = "databaseIsAvailable")
#Test public void fillData() {
// ...
}
public boolean databaseIsAvailable() {
boolean isAvailable = ...;
return isAvailable ;
}
}
This specified methods with #Prerequisite(requires = "databaseIsAvailable") must be a public method, returning a boolean or Boolean value.
If these methods will be consolidated in helper classes, you can also specify static methods within a class to be called using #Prerequisite(requires = "databaseIsAvailable", callee="DBHelper").
public class TestFillDatabase {
#Prerequisite(requires = "databaseIsAvailable", callee="DBHelper")
#Test public void fillData() {
// ...
}
}
public class DBHelper {
public static boolean databaseIsAvailable() {
boolean isAvailable = ...;
return isAvailable ;
}
}
Also using the Assume class (since jUnit 4.4), you can use assumeNoException():
try{
base.evaluate();
} catch (ConcreteException e) {
Assume.assumeNoException("Concrete exception: skipping test", e);
}
I searched for the docs about JUnit and it appears that from version 4.9 they have introduced what they call test rules (see TestRule). You may start from this.
The ExpectedException class marked as #Rule could be of some help in order to check for exceptions thrown but not mandatory for the test to pass.
For more advanced usage I cannot say for the moment as I've just discovered it.

How do I test exceptions in a parameterized test?

In JUnit4 you can write parameterized unit tests by providing parameters collection in one method, which will be passed to the constructor of the test and testing in another method. If I have a parameter for which I expect an exception to be thrown, how do I specify that?
this is how i use junit parameterized test with expected exceptions:
#RunWith(Parameterized.class)
public class CalcDivTest {
#Parameter(0)
public int num1;
#Parameter(1)
public int num2;
#Parameter(2)
public int expectedResult;
#Parameter(3)
public Class<? extends Exception> expectedException;
#Parameter(4)
public String expectedExceptionMsg;
#Rule
public ExpectedException thrown = ExpectedException.none();
#Parameters
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] {
// calculation scenarios:
{ 120, 10, 12, null, null }, // simple div
{ 120, 0, -1, ArithmeticException.class, "/ by zero" }, // div by zero
});
}
#Test
public void testDiv() throws CCalculationException {
//setup expected exception
if (expectedException != null) {
thrown.expect(expectedException);
thrown.expectMessage(expectedExceptionMsg);
}
assertEquals("calculation result is not as", expectedResult, div(num1, num2) );
}
private int div(int a, int b) {
return a/b;
}
}
In contrast to what other suggest, I would not introduce any kind of logic to tests - even simple ifs!
What you should have are two testing methods:
first one takes valid parameters (and expects some output)
second takes invalid parameters (and expects exceptions)
Not sure if JUnit with its constructor-based parametrized testing is able to do this. Probably you would have to create two test classes for this. Go with JUnit Params or TestNG which offer much more convenient solution.
I agree with Tomek, and would go with two tests. The first tests for cases where no exceptions are expected. The second tests for values that should result in exceptions being thrown (i.e., and fails if they are not thrown).
Below is a simple example, where the implementation of ExceptionThrower.throwAnInstanceException(int) simply throws an IllegalArgumentException when the supplied int is less-than-1. In your implementation, all supplied values should trigger the exception.
#ParameterizedTest
#ValueSource(ints = {0, 1})
public void parameterizedIntExceptionTest(int testValue) {
ExceptionThrower exceptionThrower = new ExceptionThrower();
assertThrows(IllegalArgumentException.class, () -> {
exceptionThrower.throwAnInstanceException(testValue);
});
}
If you wanted to supply multiple arguments, then you'd be looking at using a MethodSource vice a ValueSource for the test.
if (parameter == EXCEPTION_EXPECTED) {
try {
method(parameter);
fail("didn't throw an exception!");
} catch (ExpectedException ee) {
// Test succeded!
}
}
Gabriel, please look at TestWatcher rule (since JUnit 4.9). Here is the sample code quoted from http://junit-team.github.io/junit/javadoc/4.11/org/junit/rules/TestWatcher.html:
public static class WatchmanTest {
private static String watchedLog;
#Rule
public TestWatcher watchman= new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
watchedLog+= description + "\n";
}
#Override
protected void succeeded(Description description) {
watchedLog+= description + " " + "success!\n";
}
};
#Test
public void fails() {
fail();
}
#Test
public void succeeds() {
}
}
Another approach would be to use ErrorCollector from JUnit 4.7:
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void testCollectingErrors() {
thrown.handleAssertionErrors();
thrown.expect(MultipleFailureException.class); // or #expectMessage()/#expectCause()
collector.checkThat("a", equalTo("b"));
//...
}
If you used catch-exception instead of the corresponding annotations and rules of JUnit4, then your code would look like this:
catchException(obj).method(parameter);
if (parameter != EXCEPTION_EXPECTED) {
assert caughtException() instanceof ExpectedException;
}
// more assertions
#Test(expected = Exception.class)
#Parameters(value = { "invalidInput1", "invalidInput2" })
public void shouldThrowOnInvalidInput(String input) {
ClassToTest.methodToTest(input);
}
Using junitparams.Parameters from junitparams library.

Categories

Resources