1

I've got a class that looks like this:

public class Foo {
  public meth() {
    try {
      privMethod();
      System.out.println("Yay!");
    } catch (FooException e) {
      System.out.println("Boo!");
    }
  }

  private void privMethod() throws FooException {
    //doOtherStuff
    if (someCondition()) {
      throw new FooException();
    }
  }

  private class FooException extends Exception {
    FooException(String message) { super(message); }
  }
}

I want to write a unit test for this, using Mockito and Powermock. I know I can mock the private method like this:

Foo spy = PowerMockito.spy(new Foo())
PowerMockito.doNothing().when(spy, "privMethod");

But how do I tell it to throw the exception? I know it'll be something like this:

Powermockito.doThrow(/*what goes here?*/).when(spy, "privMethod");

What goes there?

Note that the exception is a private inner class, so I can't just do doThrow(new FooException()), since FooException isn't accessible from the unit test.

ewok
  • 20,148
  • 51
  • 149
  • 254
  • In general an inner class is an implementation detail which the test should not know (or care) about. If you have larger amounts of logic in the inner-class you should make it public and therefore testable instead. – Ben Jun 20 '18 at 13:42
  • @Ben There is no logic in the inner class. what you see above is literally all the code I have for the inner class (though the entire example is obviously very simplified. The purpose of the inner class is just to have an exception type that is distinct for the particular type of error encountered – ewok Jun 20 '18 at 13:44
  • Do you want to throw FooException or some other Exception ? is FooExceptio private inner class or you hve just put it as an example? – pvpkiran Jun 20 '18 at 13:46
  • FooException is a private inner class. – ewok Jun 20 '18 at 13:47
  • I get what you mean however I think the problem here is simply that you should not be in a position where you need to test a private method that is creating an object of an inner class. A private method is (just as the inner-class) an implementation detail. If it contains logic that needs to be tested it should be created in a testable way. – Ben Jun 20 '18 at 13:48
  • I don't understand the logic of creating a private Exception which is not a RuntimeException. How do you expect it to be caught – pvpkiran Jun 20 '18 at 13:52
  • @pvpkiran It only ever gets thrown by private methods within the same class, so it will only ever be use inside the class – ewok Jun 20 '18 at 13:54
  • When you declare a Checked Exception you expect it to be caught. Or else you would create a RunTimeException. This scenario isnt testable i am afraid. Just move the exception to a separate class. it should be fine – pvpkiran Jun 20 '18 at 13:55
  • I appreciate the quick accept! – GhostCat Jun 20 '18 at 16:01

1 Answers1

3

In general, you go.

Powermockito.doThrow(theThingToThrow)

goes there. Like doThrow(new RuntimeException("should be thrown")) for example. Or a more specific exception, depending what you want the code under test to do about the exception.

But of course: you want to instantiate a private class there. Which you can't do (easily, see here for terribly complicated ways to do that). From that point of view: you created a very-hard-to-test design. The answer is to rework that design, making it easier to test.

And a final word of warning: I think you should not need to use PowerMockito for this. Spies work with plain Mockito.

And when there is no hard need to use the PowerMock(ito) versions, then do not use them. Period. They have their place, but honestly: most often, needing PowerMock(ito) equals to "hard to test code".

Beyond that: looking at private methods translates to testing implementation details. And that is also something to avoid when possible.

So, in other words: A) it is close to impossible to test the above code in meaningful ways. B) It would also be bad practice to do so (as envisioned in the question). Therefore the "best" solution here is to step back and re-design the whole thing. Yes, I am serious.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • I think the problem is that they want to throw an object of type `FooException` which is a private inner class so they can't simply go typing `new FooException`. – Ben Jun 20 '18 at 13:44
  • @Ben True. Then he is somehow stuck. – GhostCat Jun 20 '18 at 13:47
  • 1
    Fully agree with your conclusion. It might be a rough thing to say but if you want it testable it should not be an implementation detail in the first place. – Ben Jun 20 '18 at 13:49