How to continue test after JUnit ExpectedException if thrown?
Asked Answered
S

4

23

I have set up some JUnit (4.12) test with the ExpectedException feature, and I would like the test to continue after the expected exception. But I never see the log '3', as the execution seems to stop after the exception, event if catch?

Is this actually possible, and how?

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

@Test
public void testUserAlreadyExists() throws Exception {
    log.info("1");

    // Create some users
    userService.createUser("toto1");
    userService.createUser("toto2");
    userService.createUser("toto3");
    Assert.assertTrue( userService.userExists("toto1") );
    Assert.assertTrue( userService.userExists("toto2") );
    Assert.assertTrue( userService.userExists("toto3") );

    log.info("2");

    // Try to create an existing user
    exception.expect(AlreadyExistsException.class);
    userService.createUser("toto1");

    log.info("3");
}
Sawtelle answered 6/3, 2016 at 21:59 Comment(2)
you might want to reconsider writing such a test where you want to execute something after an exception is thrown. If there's something else you want to test than you should split them into two different tests, one which checks the exception and second which checks the other logic.Charwoman
Possible duplicate of JUnit continue to assert things after expected exceptionDuchess
T
19

You cannot do that, when the exception is thrown it's thrown for real, ExpectedException rule or not.

If you really want this kind of behaviour, you can go back to the "old school" pattern:

try {
    userService.createUser("toto1");
    Assert.fail("expecting some AlreadyExistsException here")
} catch (AlreadyExistsException e) {
    // ignore
}

log.info("3");

But I wouldn't bother for some log.

Therewith answered 6/3, 2016 at 22:6 Comment(1)
OK, you confirm what I was thinking, no possible way with this. Maybe my approch isn't the best, as @thepacker describes.Sawtelle
P
3

This SO solution seems to do what you want to do: JUnit continue to assert things after expected exception

I myself was thinking something similar. To continue with the test, you would have to catch the exception yourself in the test. This solution shows an elegant way of doing that.

Note: If you make a rule to expect an exception (as you did), the test will return successful as soon as that exception is thrown. Reference: http://junit.org/javadoc/latest/org/junit/rules/ExpectedException.html

Pasteup answered 6/3, 2016 at 22:8 Comment(0)
S
2

If you don't want to add a lot of similar test methods for something that has many options to throw the expected exception and want to verify that it actually throws on all of the desired cases within a single unit-test instead, I'd suggest this (not pretty maybe) helpful schema:

@Test
public void testThatSomethingExpectedlyFails() {
    for (int i = 1; i <= 3; i++) {
        try {
            switch (i) {
                case 1: // smth here throws the exception when configuration #1;
                case 2: // smth here throws the exception when configuration #2;
                case 3: // smth here throws the exception when configuration #3;
            }
        } catch (ExceptionThatIsExpected expected) {
            continue;
        } catch (Exception unexpected) {
            /* the test must fail when an unexpected exception is thrown */                
            fail("The test has failed due to an unexpected exception: " + unexpected.getMessage()); // or just re-throw this exception
        }

        /* the test must fail when a case completes without the expected exception */
        fail("No expected exception occurred at case " + i);
    }
}

The one could also iterate items (and even execute functions) of some preliminarily prepared list instead of switch-case with hard-coded integers.

Snoopy answered 10/10, 2016 at 8:2 Comment(0)
K
1

First of all your test doesn't test one thing. It tests "userExists" and "createUser" under different conditions a.k.a. different scenarios. This is called an AssertionRoulette. You wouldn't need a hack to continue to log "3", if you would write tests, that fail fo the right reason.

If the tests fail for the right reason, you can see the scenario why it fails without doing all the logging stuff. The Junit-Runner does the logging for you already.

@Test
public void testUserExists_UserCreatedUserNotExistent_expectTrue()
{
   // Create some users
   userService.createUser("toto1");

   // Assert That user exists
   Assert.assertTrue( userService.userExists("toto1") );
}

@Test
public void testCreateUser_UserAlreadyCreated_expectAlreadyExistsExceptionIsThrown()
{
   // Create some users
   userService.createUser("toto1");

   // Try to create an existing user
   exception.expect(AlreadyExistsException.class);
   userService.createUser("toto1");    
}
Kinakinabalu answered 6/3, 2016 at 22:9 Comment(4)
As the userService uses a MongoDB behind, the problem with that approach is that one test method will already initializes data (createUser) and the other test will fail. Or should I clear the DB after each test method?Sawtelle
You could create a rule, which creates a nonce for each test and build a small servicefunction, that will create a unique name. Instead of "toto1" it will become "YzFAE4qd_toto1". You will simply have different users for each test. I saw this approach not long ago - but I do not remember where. Will add that to my answer, if I find the reference again.Kinakinabalu
You can clear the db after the class is ready. @ BeforeClass and @ AfterClass if you need to, but a Rule could cleanup the users as well. There are too many possibilities.Kinakinabalu
I have a different perspective - what if I want to write an integration test? I want to for example check that my application cleaned his stuff after exception, so your statement "First of all your test doesn't test one thing." is not always true - it depends - if you write an unit test this is reasonable, if integration test - i don't think so.Backsight

© 2022 - 2024 — McMap. All rights reserved.