Java: testing for the throwing of checked exceptions - java

I'm trying to figure out a way to build a method that will test whether a checked exception has indeed been thrown. As I was building the following minimal working example (CustomThrowablexxx are all custom types declared on their own files for readability):
package demos.exceptions;
public class TestExceptions {
// This method will check whether the provided method throws exceptions
// of the type provided.
private static boolean throwsParticularThrowable(Runnable method,
Class<Throwable> cls){
try {
method.run();
} catch(Throwable t){
if(t.getClass().equals(cls))
return true;
}
return false;
}
private static void methodOne() throws CustomThrowableOne {
throw new CustomThrowableOne("methodOne() throws");
}
private static void methodTwo() throws CustomThrowableTwo {
throw new CustomThrowableTwo("methodTwo() throws");
}
private static void methodThree() throws CustomThrowableThree {
throw new CustomThrowableThree("methodThree() throws");
}
public static void main(String[] args){
if(!throwsParticularThrowable(TestExceptions::methodOne,
CustomThrowableOne.class))
System.out.println("Nope!");
}
}
I unfortunately noticed that the access to TestExceptions::methodOne was not safe, because the compiler complained that I'm not checking for the throwing of methodOne, which I guess makes sense.
Is there any way I can automate this instead of copying and pasting the code inside throwsParticularThrowable every time?

I don't know what you are looking for but it's easier to test if exceptions are thrown using JUnit ExpectedException
https://junit.org/junit4/javadoc/4.12/org/junit/rules/ExpectedException.html

Related

Use a non-mandatory Exception to cause succeed on a junit test in java

I'm looking for a way to cause a succeed through an custom exception without expecting it all the time in junit4. Is this possible with a testrule or something, without touching every single testcase?
I know these options exist but then the exception is expected and the test fails, if no exception is thrown. I want the test to continue even if no exception is thrown and just use the exception to end the test in some special cases through aspectj.
#Test(TestSuccessException.class)
public void testCase() {
...
}
public class TestClass{
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void someTest() {
thrown.expect(MyRuntimeException.class);
...
}
}
As far as the junit4 source code looks, there isn't a way to achieve this.
The only way I found is by customizing the runner itself.
So something like this:
public class CustomTestRunner extends Runner {
private Class testClass;
public CustomTestRunner(Class testClass) {
super();
this.testClass = testClass;
}
#Override
public Description getDescription() {
return Description.EMPTY;
}
#Override
public void run(RunNotifier notifier) {
// Load all methods with #Test annotation from the given class and fire the execution
try {
Object testObject = testClass.getConstructor().newInstance();
for (Method method : testClass.getMethods()) {
if (method.isAnnotationPresent(Test.class)) {
fire(notifier, testObject, method);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void fire(RunNotifier notifier, Object testObject, Method method) throws IllegalAccessException, InvocationTargetException {
notifier.fireTestStarted(Description
.createTestDescription(testClass, method.getName()));
try {
// Call the test method
method.invoke(testObject);
} catch (InvocationTargetException e) {
// method.invoke wraps the original exception with InvocationTargetException
// The original exception is accessible via getCause()
// Check if the type of the original exception is the custom "early exist" exception
// If it doesn't match, throw the exception again; otherwise, ignore and mark the test as successful
if (!(e.getCause() instanceof EndTestEarlyException)) {
throw e;
}
}
notifier.fireTestFinished(Description
.createTestDescription(testClass, method.getName()));
}
}
You can use this by annotating the Test class as follows:
#RunWith(CustomTestRunner.class)
class MyIntegrationTest {
...
}
Note: Runner is the most generic Runner possible.
You could also attempt overriding a more specific runner if you already use one.
Edit:
As you are working with legacy, I intentionally tried not to use newer language features, like generics (Class<?>).
The solution is based on this baeldung article.
Junit5
Last but not least:
This is probably not relevant in your particular case but might be interesting for future readers.
If you manage to upgrade to Junit5, you could handle this within an extension.
You could implement a custom extension like this:
public class IgnoreEndTestEarlyException implements TestExecutionExceptionHandler {
#Override
public void handleTestExecutionException(ExtensionContext context,
Throwable throwable) throws Throwable {
if (throwable instanceof EndTestEarlyException ) {
return;
}
throw throwable;
}
}
And use it like this:
#ExtendWith(IgnoreEndTestEarlyException.class)
public class MyIntegrationTest
I tend to create another annotation (something like #IntegrationTest), put the #ExtendsWith on there, and use the new annotation.
It would be cleaner and easier to add multiple extensions.
You can run Junit4 and Junit5 within the same module, but you must replace all annotations within your integration test suit.
It might not be worth the effort for multiple thousand tests.
For Junit4 I found a better solution for my usecase. Just override the runChild Method from BlockJUnit4ClassRunner and add a try catch for the EndTestEarlyException.
#Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
Statement statement = new Statement() {
#Override
public void evaluate() throws Throwable {
try {
methodBlock(method).evaluate();
} catch (EndTestEarlyException e) {
System.out.println("EndTestEarlyException - ignore");
}
}
};
runLeaf(statement, description, notifier);
}
}

java exception catching in a different class than throwing

I have been working on a code that does 2 things:
Has a class that performs computations (logic)
has a class that displays the result.
I am wondering if it is possible to use try/catch statements in the Display class, where I would attempt to catch exceptions originating in the logic class.
Where Display would execute a line similar to logic.execute(input);
I was able to create a custom exception class where the following is placed in display class:
try{
logic.execute(input);
}catch(CustomException e){
//print statements
}
However I would like to be able to print exactly the error that occured, such as NullPointerException.
When i say print, i mean output in console. (but it must originate from display class)
If such a monstrosity is possible, please let me know.
Thank You guys!
Yes, it's possible.
You will need your custom exception class to extend RuntimeException instead of Exception, or the compiler will complain that you are not catching the exception that you throw.
See this post: Throwing custom exceptions in Java
Simple working example:
public class ExceptionTest
{
public static void main(String[] args)
{
SomeClass myObject = new SomeClass();
myObject.testFunction();
}
}
public class SomeClass
{
private SomeOtherClass someOther = new SomeOtherClass();
public void testFunction()
{
try{
someOther.someOtherFunction();
}
catch(Exception e){
System.out.println(e.toString());
}
}
}
public class SomeOtherClass
{
public void someOtherFunction()
{
throw new CustomException("This is a custom exception!");
}
}
public class CustomException extends RuntimeException
{
public CustomException(String message)
{
super(message);
}
}

JUnit 4 - expect an Exception of a certain class, but not subclasses

I'll try to provide a hackneyed, useless example that reduces the problem nicely :-)
I have a GenericException, and a MoreSpecificException which extends GenericException.
I need to test that SomeService.doThis() throws a MoreSpecificException. JUnit lets me do this elegantly like so.
#Test(expected = MoreSpecificException.class)
public void testDoThis() throws GenericException {
new SomeService().doThis();
}
However, I also need to test that SomeService.doThat() throws a GenericException, so I tried this.
#Test(expected = GenericException.class)
public void testDoThat() throws GenericException {
new SomeService().doThat();
}
However, I found that if doThat() actually throws a MoreSpecificException then the second test still passes. I assume this is because MoreSpecificException is a GenericException and the annotation is implemented to respect that relationship.
While this is a sensible default behaviour, I don't want this. I want to test that doThat() throws a GenericException and only a GenericException. If it throws a MoreSpecificException or any other subclass of GenericException, I want the test to fail.
Reading the docs it doesn't seem I can do anything with the annotation to change this behaviour, so looks like I'll have to use another solution.
At the moment I'm resorting to the following ugly solution - EDIT made significantly less ugly by Nathan Hughes' answer :-)
#Test
public void testDoThat() {
try {
new SomeService().doThat();
Assert.fail();
} catch(GenericException ex) {
Assert.assertEquals(GenericException.class, ex.getClass());
}
}
Is there a more elegant way to achieve what I want within the JUnit framework?
BDD Style Solution
JUnit 4 + Catch Exception + AssertJ
The most elegant solution ;) Readable, without boilerplate code.
#Test
public void testDoThat() {
when(new SomeService()).doThat();
then(caughtException()).isExactlyInstanceOf(GenericException.class);
}
The code is identical for FEST Assertions 2 + Catch-Exceptions.
Source code
https://gist.github.com/mariuszs/7489706
Dependencies
org.assertj:assertj-core:1.4.0
com.googlecode.catch-exception:catch-exception:1.2.0
You can assert that the class of the Exception is what you expect:
#Test
public void testDoThat() {
try {
new SomeService().doThat();
Assert.fail();
} catch(GenericException ex) {
assertEquals(GenericException.class, ex.getClass());
}
}
Also got rid of the flag, instead having the test fail if no exception is thrown.
You can use the ExpectedException rule and a custom Hamcrest matcher that specifies which class can be thrown.
The following test will print that you expected an instance of RuntimeException, but got an IllegalArgumentException.
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void testThrows() {
thrown.expect(isClass(RuntimeException.class));
throw new IllegalArgumentException("FAKE");
}
public class ClassMatchMatcher extends BaseMatcher<Object> {
private final Class<?> expectedClass;
private ClassMatchMatcher(Class<?> expectedClass) {
this.expectedClass = expectedClass;
}
#Override
public boolean matches(Object item) {
return expectedClass.equals(item.getClass());
}
#Override
public void describeTo(Description description) {
description.appendText("an instance of ")
.appendText(expectedClass.getName());
}
}
public class ExtraMatchers {
public static Matcher<Object> isClass(Class<?> aClass) {
return new ClassMatchMatcher(aClass);
}
}
Edit: Added a static factory method to make the test code cleaner.

PowerMock + EasyMock: private void method without invokation

Good time!
I need to substitute the class' private void method with a mock implementation, and can't to figure out how to do this. I've tried to use such a construction:
Test test = PowerMock.createPartialMock(Test.class, "setId");
PowerMock.expectPrivate(test , "setId", EasyMock.anyLong()).andAnswer(
new IAnswer<Void>() {
#Override
public Void answer() throws Throwable {
return null;
}
});
PowerMock.replay(test);
but the internal PowerMock's class called WhiteBox invokes my "setId" method which is wrong for my task. Could someone, please, suggest, how to avoid the method invokation and possibly to replace the method body with a custom one?
Finally. I've got the solution.
The problem was I missed the following annotations:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Test.class)
Anyway, it seems rather confusing that to make the PowerMock working I need to add some annotations. If that wasn't a legacy code I'd prefer Mockito.
Not quite sure that I get the question.
But for me code below works perfect and just string "Invoked!" is getting printed
and if I remove test.setS(33L); test will fail with exception:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainTest.Test2.class)
public class MainTest {
#Test
public void testName() throws Exception {
Test2 test = PowerMock.createPartialMock(Test2.class, "setS");
PowerMock.expectPrivate(test , "setS", EasyMock.anyLong()).andAnswer(
new IAnswer<Void>() {
#Override
public Void answer() throws Throwable {
System.out.println("Invoked!");
return null;
}
}).atLeastOnce();
PowerMock.replay(test);
test.setS(33L);
PowerMock.verify(test);
}
class Test2 {
long s;
private long getS() {
return s;
}
private void setS(long s) {
this.s = s;
System.out.println("Not this!");
}
}
}

Throwing exceptions and catching them elsewhere

I was wondering if it was possible to write a method to throw an exception and have another method catch these exceptions.
For example,
public static void checkCircle() {
try {
checkPixel(a);
checkPixel(b);
checkPixel(c);
} catch (MyException e) {
System.out.println("not circle");
}
private static void checkPixel(anything) {
if (img.getRGB(xValue, yValue) != pOrigColour) {
throw new MyException();
}
}
class MyException extends Exception {
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
Thing is I want to the checkPixel method to throw a MyException, indicating that there is no circle, regardless of the results of the other calls.
Yes, it is possible. In your method declaration, you can add a throws clause, which indicates that your method might throw an exception.
In your case, you should modify your method declaration like this:
private static void checkPixel(anything) throws MyException {
// ...
}
You should note that exceptions should be used for... exceptional situations. Using them for simple error handling is highly unconventional, and adds unnecessary burden on users of your classes. In your case, you might want to add a boolean hasCircleAtLocation () method that would return true if there is a circle at the provided location.

Categories

Resources