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

19,033

Solution 1

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?

Solution 2

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;
Share:
19,033
asmb
Author by

asmb

Updated on June 05, 2022

Comments

  • asmb
    asmb almost 2 years

    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)