I have a piece of code in my android app that catches one exception and rethrows it as another, custom exception extended from RuntimeException. And it seems impossible to be tested.
Take a look at the following code:
try { ... } catch (MalformedURLException e) {
throw new CustomMalformedDataException(downloadUrlString, e);
}
When I try to test the exception case, I write:
#Test(expected = CustomMalformedDataException.class)
public void exceptionsTest() throws Exception {
<code triggering the exception>
}
But i get Test running failed: Instrumentation run failed due to 'java.net.MalformedURLException'
When I write:
#Test(expected = MalformedURLException.class)
public void exceptionsTest() throws Exception {
<code triggering the exception>
}
I get java.lang.Exception: Unexpected exception, expected<java.net.MalformedURLException> but was<CustomMalformedDataException>
So how should I test this case?
Code of exception class:
public class CustomMalformedDataException extends RuntimeException {
public CustomMalformedDataException(String message, Throwable cause) {
super(message, cause);
}
public CustomMalformedDataException(String url, MalformedURLException cause) {
super("The package has an invalid \"downloadUrl\": " + url, cause);
}
}
UPDATE: It seems that either way tests stop execution at the point where the initial exception is thrown, even though it is caught. But in case this exception is expected, the execution continues and throws another exception, which is already not expected.
So I tried out your code and got a different error so I can't give you an answer, but I've got a way of testing errors I prefer using JUnit that will get you the results you need
#Rule
public final ExpectedException exception = ExpectedException.none();
#Test
public void testExceptionOccurs() throws Exception {
exception.expect(CustomMalformedDataException.class);
//code that casues exception to happen
}
I've run this plenty of times and it works with no issues when I need to test that my exceptions are occuring. I hope this at least helps giving you a solution to writing exception tests.
If you really need to test RuntimeException then have your test throw RuntimeException instead of Exception
Related
When I remove the try/catch it works but cannot test negative test
public class TileCombinationSetsTest {
#Test public void testTileCombinations() {
new TileCombinationSets();
assertEquals(TileCombinationSets.tileCombinations(1).size(), 7);
assertEquals(TileCombinationSets.tileCombinations(2).size(), 42);
assertEquals(TileCombinationSets.tileCombinations(3).size(), 210);
assertEquals(TileCombinationSets.tileCombinations(4).size(), 840);
assertEquals(TileCombinationSets.tileCombinations(5).size(), 2520);
assertEquals(TileCombinationSets.tileCombinations(6).size(), 5040);
assertEquals(TileCombinationSets.tileCombinations(7).size(), 5040);
try {
TileCombinationSets.tileCombinations(4);
fail("Exceptions expected");
}
catch(Throwable e) {}
}
}
In JUnit a test fails when the test method throws an exception (or an other Throwable). JUnit's test runner catches the exception and reports the test as failed. On the other hand the test runner considers a test to be successful when the test method finishes without throwing an exception. Statements like assertEquals and fail throw an AssertionError.
In your test fail("Exceptions expected") is throwing an AssertionError. which is immediately caught and and therefore the test method testTileCombinations doesn't throw an exception. Now for JUnit it looks like the method was executed successfully and therefore it considers the test to be successful.
If you want to test that TileCombinationSets.tileCombinations(4) throws an exception then you can use JUnit's assertThrows
assertThrows(
Exception.class, // you can be more specific here
() -> TileCombinationSets.tileCombinations(4)
);
If you want to test that TileCombinationSets.tileCombinations(4) doesn't throw an exception then you simply execute it. (JUnit will report the test as failed if it throws an exception and successful otherwise.)
#Test
public void testTileCombinations() {
...
TileCombinationSets.tileCombinations(4);
}
I assume the part
TileCombinationSets.tileCombinations(4)
throws an exception the second time it is executed.
You could print the stacktrace of the exception to see if an exception is thrown.
Without knowing what TileCombinationSets.tileCombinations(int x) does its hard to say.
If possible could you post the method?
You should generally not catch Throwable but Exception instead. Throwable also includes subclasses of Error, which are critical errors concerning that Java Runtime Environment itself (like OutOfMemoryError), from which a program should not attempt to recover because it puts the program into an undefined state.
As it happens, the AssertionError that is thrown by the fail() method in your example
try {
TileCombinationSets.tileCombinations(4);
fail("Exceptions expected");
}
catch(Throwable e) {}
also extends Error.
So by changing your code to
try {
TileCombinationSets.tileCombinations(4);
fail("Exceptions expected");
}
catch(Exception e) {}
it should behave as intended, as long as the exceptions thrown by your tileCombinations() method only extend Exception (which they should, as it is bad practice to extend Error yourself).
#Override
Public class example {
void test {
try {
someMethod(); //This throws TimeoutException
} catch (TimeoutException ex) {
throw new TimeoutException(ex); //It doesn't throw error if I replace this with throw new RuntimeException(ex)
} }
}
The above example gives an error as 'throw new TimeoutException(ex)' as "TimeoutException(java.lang.string) in java.util.concurrent.TimeoutException cannot be applied to (java.util.concurrent.TimeoutException)".
But it doesn't throw an error if I replace it with 'throw new RuntimeException(ex)';
TimeoutException doesn't have a constructor that accepts a TimeoutException as an argument of the form TimeoutException(TimeoutException cause) or similar.
You can instead:
TimeoutException localtoe=new TimeoutException("test failed");
localtoe.initCause(ex);
throw localtoe;
Or equally:
throw new TimeoutException("test failed").initCause(ex);
initCause() may only be called once and only if cause wasn't set by a constructor. It's a funny little method that acts like a constructor after-thought(*).
There's nothing necessarily wrong with wrapping an exception as the cause of an exception.
Suppose testFunction() connects and then performs some operation.
You might want to throw an exception with message "connection failed in testFunction" and another "operation failed in testFunction" depending on what sub-step failed.
But if you don't need to provide so much detail you can just throw ex or let the method unwind without itself catching anything.
Here's a little example:
import java.util.concurrent.TimeoutException;
class Example{
private static void connect() throws TimeoutException {
//Dummy connection that just fails...
throw new TimeoutException("connection failed");
}
private static void process() throws TimeoutException {
try {
connect();
}catch(TimeoutException toe){
TimeoutException toeout=new TimeoutException("process failed because connection failed.");
toeout.initCause(toe);
throw toeout;
}
//Code for when connection succeeds...
}
public static void main (String[] args) throws java.lang.Exception
{
try{
process();
}catch(TimeoutException toe){
System.out.println(toe);
}
}
}
Expected output:
java.util.concurrent.TimeoutException: process failed because connection failed.
(*) initCause() looks like an after-thought and is somewhat. It was added to Java 1.4 in 2002. The documentation talks about 'legacy' constructors. Rather than double up the number of constuctors (to add one with a Throwable cause argument) it appears it was decided to allow this as bolt-on initialization.
It's debatable whether that was the best solution.
Things I observed in your question
you are trying to call a method directly from class in a try cache block. which is wrong you have to create method and call it from that
you want to throw an exception. SO you have to throw it method level from where you are calling it
please find the demo solution below :
public class example {
public void testFunction() throws TimeoutException {
try {
someFunction();
} catch (TimeoutException ex) {
throw ex;
}
}
public void someFunction() throws TimeoutException {
}
}
Java has 2 categories of exceptions: checked and unchecked. Checked exception (usually subclasses of Exception) must be declared in function signatures, while unchecked ones (usually subclasses of RuntimeException) must not.
TimeoutException is a checked exception. When it could be thrown from a method that does not declare it, you have 2 options:
declare it in the signature:
public void func1() throws TimeoutException {
somefunction();
}
clean and simple but it can be problemetic is func1 is an override of a function not declared to throw this exception, of it it is called from another function (suppose from a framework) that does not declare it either
hide it in an unchecked exception
public void func1() {
try {
somefunction();
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}
you lose the declarative part (checked exceptions exist for that reason), but at least it allows you to call it from function not declaring it.
You have roughly three options here:
Rethrow the same exception: `throw ex;'
Throw a new TimeoutException and lose the stack trace: throw new TimeoutException(ex.getMessage());
Throw an exception of another type, such as RuntimeException.
Each of these options have advantages and drawbacks, you decide.
UPDATE (thanks to #Mark Rottenveel)
Point 2 could be rewritten: throw new TimeoutException(ex.getMessage()).initCause(ex); to keep the link to the original exception.
I am trying to write a test case for a method which throws an exception based on certain logic. However the test case fails as the expected exception and obtained exceptions are different.
Method to test -
public void methodA (//parameters) throws ExceptionA
{
certainlogic=//call some method
if (certainlogic)
throw new ExceptionA(//exception details)
else
//code snippet
}
Test method -
#Test (expected=ExceptionA.class)
public void testMethodA
{
try
{
when (//mock method).thenReturn(true);
//call methodA
}
catch (ExceptionA e)
{
fail(e.printStackTrace(e));
}
}
I am receiving the below error -
Unexpected exception, expected<cExceptionA> but was<java.lang.AssertionError>
How do I solve this issue?
You have to remove the catch in your test
#Test (expected=ExceptionA.class)
public void testMethod()
{
when (//mock method).thenReturn(true);
//call methodA
}
Otherwise you catch the ExceptionA and by calling fail you throw an AssertionError. Obviously the AssertionError is not an ExceptionA and therefore your test fails.
You should remove the try-catch block entirely or at least the catch.
The "expected = ExceptionA.class" tells junit to monitor for thrown exceptions, catch them and compare their class against the given class.
If you catch the thrown exception, the #Test-annotated junit method cannot detect if such an exception is thrown.
By calling fail(...) you implicitly throw an AssertionError which junit detects and thus your test fails because AssertionError.class != ExceptionA.class
Can anyone please give me information about it. I cant really uderstand the type of this Exception.
Thank you
public class ValidationException extends Exception{
public ValidationException(){
super("There was a problem when validating data");
}
public ValidationException(String message){
super(message);
}
public ValidationException(String message, Throwable throwable){
super(message, throwable);
}
public ValidationException(Throwable throwable){
super(throwable);
}
}
It is a "runtime exception" in the (fatuous) sense that it is an exception that occurs at runtime. But that is true for all Java exceptions ... apart from bugs in the compiler, etcetera.
It is not a subclass of RuntimeException. You have declared it as a subclass of Exception and Exception is not a subclass of RuntimeException. (In fact, the reverse is true: RuntimeException is a subclass of Exception!)
It is a checked exception because it is not a subclass of RuntimeException (or Error).
Since it is a checked exception, the Java rules about checked exceptions apply. For example, any method that throws or propagates1 your exception must declare that it throws this exception, or an exception that is a superclass of this exception.
1 - Technically, the JLS describes this as an abnormal termination of the method body with this exception as the abnormal termination reason.
The main difference between Exception, and RuntimeException is that we need to wrap a Exception in a try/catch block. A RuntimeException does not need to be caught, but it is just as lethal as an Exception.
public class Main{
public static void main(String[] args) {
Thread.currentThread().setUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler(){
#Override
public void uncaughtException(Thread t, Throwable e){
System.out.println("Uncaught Exception " + e);
}
});
try{
throwException();
}catch(Exception e){
System.out.println("Caught Exception " + e);
}
try{
throwRuntimeException();
}catch(Exception e){
System.out.println("Caught RuntimeException " + e);
}
//unchecked, no need to wrap int try/catch
throwRuntimeException();
}
public static void throwException() throws Exception {
throw new Exception();
}
public static void throwRuntimeException() {
throw new RuntimeException();
}
}
Take this example above. The output is this:
Caught Exception java.lang.Exception
Caught RuntimeException java.lang.RuntimeException
Uncaught Exception java.lang.RuntimeException
As you can tell, the call to throwRuntimeException() gets thrown, and since there is no try/catch block it has no idea how to handle it. This crashes the thread and since there is an UncaughtExceptionHandler it gets called.
Then there is also Error which I won't go into since I don't know much about it besides that JVM throws it. OutOfMemoryError is an example.
I wrote some test cases to test some method. But some methods throw an exception. Am I doing it correctly?
private void testNumber(String word, int number) {
try {
assertEquals(word, service.convert(number));
} catch (OutOfRangeNumberException e) {
Assert.fail("Test failed : " + e.getMessage());
}
}
#Test
public final void testZero() {
testNumber("zero", 0);
}
If I pass -45, it will fail with OutOfRangeException but I am not able to test specific exception like #Test(Expected...)
An unexpected exception is a test failure, so you neither need nor want to catch one.
#Test
public void canConvertStringsToDecimals() {
String str = "1.234";
Assert.assertEquals(1.234, service.convert(str), 1.0e-4);
}
Until service does not throw an IllegalArgumentException because str has a decimal point in it, that will be a simple test failure.
An expected exception should be handled by the optional expected argument of #Test.
#Test(expected=NullPointerException.class)
public void cannotConvertNulls() {
service.convert(null);
}
If the programmer was lazy and threw Exception, or if he had service return 0.0, the test will fail. Only an NPE will succeed. Note that subclasses of the expected exception also work. That's rare for NPEs, but common with IOExceptions and SQLExceptions.
In the rare case that you want to test for a specific exception message, you use the newish ExpectedException JUnit #Rule.
#Rule
public ExpectedException thrown= ExpectedException.none();
#Test
public void messageIncludesErrantTemperature() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("-400"); // Tests that the message contains -400.
temperatureGauge.setTemperature(-400);
}
Now, unless the setTemperature throws an IAE and the message contains the temperature the user was trying to set, the test fails. This rule can be used in more sophisticated ways.
Your example can best be handled by:
private void testNumber(String word, int number)
throws OutOfRangeNumberException {
assertEquals(word, service.convert(number));
}
#Test
public final void testZero()
throws OutOfRangeNumberException {
testNumber("zero", 0);
}
You can inline testNumber; now, it does not help much. You can turn this into a parametrized test class.
Remove the try-catch block and add throws Exception to your test method, like:
#Test
public final void testZero() throws Exception {
assertEquals("zero", service.convert(0));
}
JUnit expects failing tests will throw Exceptions, your catching them is just stopping JUnit from being able to report them properly. Also this way the expected property on the #Test annotation will work.
You don't need to catch the exception to fail the test. Just let it go (by declaring throws) and it will fail anyway.
Another case is when you actually expect the exception, then you put fail at the end of try block.
For example:
#Test
public void testInvalidNumber() {
try {
String dummy = service.convert(-1));
Assert.fail("Fail! Method was expected to throw an exception because negative numbers are not supported.")
} catch (OutOfRangeException e) {
// expected
}
}
You can use this kind of test to verify if your code is properly validating input and handles invalid input with a proper exception.
There are several strategies that are open to you to deal with expected exceptions in your tests. I think the JUnit annotations and try/catch idiom have already been mentioned above. I'd like to draw attention to the Java 8 option of Lambda expressions.
For instance given:
class DummyService {
public void someMethod() {
throw new RuntimeException("Runtime exception occurred");
}
public void someOtherMethod(boolean b) {
throw new RuntimeException("Runtime exception occurred",
new IllegalStateException("Illegal state"));
}
}
You can do this:
#Test
public void verifiesCauseType() {
// lambda expression
assertThrown(() -> new DummyService().someOtherMethod(true))
// assertions
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasCauseInstanceOf(IllegalStateException.class);
}
Take a look at this blog which covers most of the options with examples.
http://blog.codeleak.pl/2013/07/3-ways-of-handling-exceptions-in-junit.html
And this one explains the Java 8 Lambda option more fully:
http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html