2

I have a method which I should test:

public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException {
    final Node parentNode = this.parentNodeStack.peek();
    boolean isParentImport = (name == null && isParentNodeImport);
    if (name == null) {
        if (this.parentNodeStack.size() > 1) {
            throw new RepositoryException("Node needs to have a name.");
        }
        name = this.defaultName;
    }
    //other stuff
}

In my test I put into method parameters with which method should throw a RepositoryException(Line 6).

@RunWith(JMock.class)
public class DefaultContentCreatorTest {

    static final String DEFAULT_NAME = "default-name";
    final Mockery mockery = new JUnit4Mockery();
    DefaultContentCreator contentCreator;

    Session session;
    Node parentNode;
    Property prop;

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

    @Before
    public void setup() throws Exception {
        final SlingRepository repo = RepositoryProvider.instance().getRepository();
        session = repo.loginAdministrative(null);
        contentCreator = new DefaultContentCreator(null);
        contentCreator.init(U.createImportOptions(true, true, true, false, false),
                new HashMap<String, ContentReader>(), null, null);
        parentNode = session.getRootNode().addNode(getClass().getSimpleName()).addNode(uniqueId());
    }

    @After
    public void cleanup() throws RepositoryException {
        if(session != null) {
            session.save();
            session.logout();
            session = null;
        }
    }

    @Test
    public void createNodeWithoutNameAndTwoInStack() throws RepositoryException {
        contentCreator.init(U.createImportOptions(true, true, true, false, false),
                new HashMap<String, ContentReader>(), null, null);
        //Making parentNodeStack.size() == 1
        contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
        //Making parentNodeStack.size() == 2
        contentCreator.createNode(uniqueId(), null, null);

        //contentCreator.createNode method should now throw an exception
        thrown.expect(RepositoryException.class); //Doesn't work
        thrown.expectMessage("Node needs to have a name."); //Doesn't work
        contentCreator.createNode(null, null, null);
    }
}

However test fails with exactly this RepositoryException. What I'm doing wrong? BTW if I use @Test(expected = RepositoryException.class) everything works fine.

UPD: I'm writing unit test for one of Apache Sling classes. You can take a look on this class here UPD2: There is a failing's test exception stack trace:

javax.jcr.RepositoryException: Node needs to have a name.
at org.apache.sling.jcr.contentloader.internal.DefaultContentCreator.createNode(DefaultContentCreator.java:225)
at org.apache.sling.jcr.contentloader.internal.DefaultContentCreatorTest.createNodeWithoutNameAndTwoInStack(DefaultContentCreatorTest.java:278)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.jmock.integration.junit4.JMock$1.invoke(JMock.java:37)
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:107)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:100)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:61)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:54)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:52)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
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:39)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

As you can see it throws exception at line 278 which is the line with contentCreator.createNode(null, null, null); method call.

Petr Shypila
  • 1,449
  • 9
  • 27
  • 47
  • 1
    Can you give a complete and reproducible example? – Sotirios Delimanolis Jun 11 '15 at 16:08
  • Just debug this org.junit.rules.ExpectedException.ExpectedExceptionStatement#evaluate – AdamSkywalker Jun 11 '15 at 16:12
  • @SotiriosDelimanolis Done. – Petr Shypila Jun 11 '15 at 16:19
  • Test is failing because code above setting the expected exception details is throwing an exception. Show us full stacktrace. – Arek Jun 11 '15 at 16:20
  • @PetrShypila Wrap `contentCreator.createNode(null, null, null)` within a try-catch block, remove `@Rule` and then confirm that test is passing. Have you debug this code? – Arek Jun 11 '15 at 16:40
  • @Ajan yes, I have. And it throws where I expect. I see that all expected configs are configured in `thrown` object (I mean exception and its message). By the way `thrown` object contains two properties `handleAssumptionViolatedExceptions` and `handleAssertionErrors`. Both of them are set to `false`. – Petr Shypila Jun 11 '15 at 16:45
  • 1
    Hm... maybe the `JMock` runner is messing something... Please try to remove `@RunWith(JMock.class) – Arek Jun 11 '15 at 16:52
  • @Ajan Finally you were right. With our JMock's runner test passes fine. Thank you very much. – Petr Shypila Jun 11 '15 at 16:56
  • @PetrShypila U R welcome ;) – Arek Jun 11 '15 at 16:59

2 Answers2

3

Just to summarize discussion from comments: the solution was to removeJMock jUnit runner.

Arek
  • 3,106
  • 3
  • 23
  • 32
  • You must be using an old version of JMock. Newer versions extend `BlockJUnit4ClassRunner` (not `org.junit.internal.runners.JUnit4ClassRunner`) and would therefore support rules – NamshubWriter Jun 12 '15 at 05:36
-1

Try placing your thrown.expect before the actual call that you expect an exception from

@Test
public void createNodeWithoutNameAndTwoInStack() throws RepositoryException {
    thrown.expect(RepositoryException.class); //Doesn't work
    thrown.expectMessage("Node needs to have a name."); //Doesn't work 

    contentCreator.init(U.createImportOptions(true, true, true, false, false),
            new HashMap<String, ContentReader>(), null, null);

    //Making parentNodeStack.size() == 1
    contentCreator.prepareParsing(parentNode, DEFAULT_NAME);

    //Making parentNodeStack.size() == 2
    contentCreator.createNode(uniqueId(), null, null);

    // method should now throw an exception
    contentCreator.createNode(null, null, null);
}
VGR
  • 40,506
  • 4
  • 48
  • 63
malificent
  • 1,441
  • 14
  • 18
  • It's a wrong idea because now you don't know what line should throw the exception. It would be much better if there would be old-fashioned try-catch ... – Arek Jun 11 '15 at 16:22
  • Thank you for your answer. However it doesn't help me. My method still fails with this exception. BTW I also thought that I should call ExpectedException#expect() one line before a method which this exception will throw. – Petr Shypila Jun 11 '15 at 16:23
  • @Ajan...but wouldn't the last line of the test always be where you'd expect the exception? I can't see any reason to continue with the test if you're expecting an exception from a particular method call. It's probably just a matter of preference but I think using the ExpectedException class is cleaner than using a try-catch. Declaring it at the top gives clear indication of what's expected from the test in my opinion. – malificent Jun 11 '15 at 18:52
  • @malificent : It gives clear indication of what's expected but... you are losing information about *where it's expected*. – Arek Jun 12 '15 at 05:17
  • @Ajan: where else would the exception to be expected except for the last line of the test? If you expect an exception at a particular point, there is no reason to continue with the test, correct? – malificent Jun 12 '15 at 14:47
  • @malificent: I agree but if you expect exception on the last line and exception (from some reason) would be thrown from any of the previous lines then the test would stil pass. – Arek Jun 12 '15 at 18:16
  • @Ajan: I don't think that's true. The test will fail with an error...http://stackoverflow.com/questions/3425995/whats-the-difference-between-failure-and-error-in-junit – malificent Jun 12 '15 at 21:46
  • @malificent There are many resources available online about this problem, ex. http://jakegoulding.com/blog/2012/09/26/be-careful-when-using-junit-expected-exceptions/ – Arek Jun 14 '15 at 08:18