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");
}
Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
Deathtiny
  • 728
  • 3
  • 8
  • 14
  • 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. – Rahul Sharma Mar 06 '16 at 22:08
  • 1
    Possible duplicate of [JUnit continue to assert things after expected exception](http://stackoverflow.com/questions/21506079/junit-continue-to-assert-things-after-expected-exception) – Joe Mar 07 '16 at 11:00

4 Answers4

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.

  • OK, you confirm what I was thinking, no possible way with this. Maybe my approch isn't the best, as @thepacker describes. – Deathtiny Mar 07 '16 at 14:49
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

Community
  • 1
  • 1
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.

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");    
}
thepacker
  • 119
  • 5
  • 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? – Deathtiny Mar 07 '16 at 14:47
  • 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. – thepacker Mar 07 '16 at 16:16
  • 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. – thepacker Mar 07 '16 at 17:01
  • 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. – Nicramus May 30 '19 at 15:32