JUnit: Test exception doesn't work (AssertionError: Expected exception even if exception thrown)

21.7k views Asked by At

I'm trying to test my class for a Exception. I've been trying a couple different methods, nothing works. What am I doing wrong here?

The class I'm trying to test, PrimeNumber.java:

public class PrimeNumber {

    static final Logger LOG = LogManager.getLogger("Log");

    private String primeNumberStr;

    public PrimeNumber(String primeNumberStr) {
        this.primeNumberStr = primeNumberStr;
    }

    public String getPrimeResult() {
        String resultStr = "";
        try {
            // Convert user input to int
            int primeNumberInt = Integer.parseInt(primeNumberStr);
            // Beginning of return message
            resultStr += primeNumberInt + " is ";
            // Add "not" if it's not a prime
            if (!Primes.isPrime(primeNumberInt))
                resultStr += "NOT ";
            // End return message
            resultStr += "a prime";
            // If not a valid number, catch
        } catch (NumberFormatException e) {
            // Log exception
            LOG.warn("NumberFormatException" + e.getMessage());
            // If empty user input
            if (primeNumberStr.length() == 0)
                resultStr += "No number inserted";
            // Else not empty but not valid
            else
                resultStr += primeNumberStr + " is not a valid number";
            resultStr += ". Only numbers without decimals are accepted.";
        }

        return resultStr;
    }

}

And now things I've tried to test:

With annotation

@Test(expected = NumberFormatException.class)
public void testNumberFormatExceptionBeingThrown() {
    PrimeNumber primeNumber = new PrimeNumber("6dg");
    primeNumber.getPrimeResult();
}

Results in failed test and:

java.lang.AssertionError: Expected exception: java.lang.Exception

With a JUnit rule:

@Rule public ExpectedException thrown = ExpectedException.none();

@Test(expected = NumberFormatException.class)
public void testNumberFormatExceptionBeingThrown() {
    thrown.expect(NumberFormatException.class);
    thrown.expectMessage("For input string: \"a21\"");
    PrimeNumber primeNumber = new PrimeNumber("a21");
    primeNumber.getPrimeResult();
}

Results in:

java.lang.AssertionError: Expected test to throw (an instance of java.lang.NumberFormatException and exception with message a string containing "For input string: \"a21\"")
at org.junit.Assert.fail(Assert.java:88)
at org.junit.rules.ExpectedException.failDueToMissingException(ExpectedException.java:263)
at org.junit.rules.ExpectedException.access$200(ExpectedException.java:106)
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:245)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

2

There are 2 answers

0
Dan Getz On BEST ANSWER

Everything's working as it should. You've successfully been able to prevent your methods from throwing exceptions. You've successfully been able to test that they do throw exceptions.

It's just, it doesn't make sense to do both at the same time. You need to decide if you want each method to throw an exception or not when given wrong arguments.

If you do want a method to throw an exception when given a wrong argument, then don't catch and handle the exception, just let it be thrown. Then, test that your method throws that exception, like you did above.

If you don't want the method to throw an exception when given a wrong argument, then decide what you want it to do instead. Then, test that your method does what you want it to. If it throws an exception, the test will fail.


That said, the way you handle numbers in your class doesn't make much sense. You're getting them as Strings, storing them as Strings, and returning them as Strings, but whenever you work with them, you convert them back and forth to ints. Why not just use int everywhere in the first place?

0
Makoto On

Your getPrimeResult is actually dealing with the exception as opposed to allowing it to propagate any further in the call stack. Hence, it's not being thrown, and the test will never be able to intercept it and assert against it (in that state).

While I think that this is a better approach, if you want to propagate it through the call stack, you would need to throw it after you've caught it.

// after the assignment to resultStr
throw e;