0

I have a class structure similar to the following

 public abstract class AbstractStep {
     private final Range RANGE;

     AbstractStep(AbstractStepBuilder builder) {
          RANGE = builder.range;   
     }


      public abstract static class AbstractStepBuilder {
           Range range;

           public AbstractStepBuilder setRange(int start, end end) {
                this.range = new Range(start, end);
                return self();
           }

           abstract AbstractStepBuilder self();
      }

      public static class Range() {
           private final int START;
           private final int END;

           private Range(int start, int end) {
                if(start < 0 || end < 0 || start >= end)
                      throw new IllegalArgumentException();
                START = start;
                END = end;
           }
      }
 }

I want to test setRange(int, int) in AbstractStepBuilder to see if the an IllegalArgumentException is thrown. I use TestNG and Mockito, and I have attempted the following using with the help of this.

 final class RangeTest {
      AbstractStepBuilder builder;

      @BeforeSuite 
      void setup() {
           builder = Mockito.mock(AbstractStepBuilder.class);
           Mockito.when(builder.self()).thenReturn(null);
       }

     @Test(expectedExceptions = IllegalArgumentException.class)
     final void testCreatingRangeWithNegativeStart() {
          builder.setRange(-1, 2);
     }
}

This test fails. I have also tried replacing Mockito.mock(AbstractStepBuilder.class) with Mockito.mock(AbstractStepBuilder.class, Mockito.CALLS_REAL_METHODS) as in the top answer of this question.

Note that if I make CodeRange as its own outer class, this test passes, so I do not believe it could be the test itself.

Why is this test failing, and is it possible to fix it without having to use a concrete class in the test instead?

Lindstorm
  • 890
  • 2
  • 7
  • 22

1 Answers1

2

You're calling a method on a mock, that will never throw an Exception until you tell it to. You never mock a class you want to test.

If you want to test the actual class, you'll need to create a subclass of the step builder, create an instance and test that.

I think you can also create a spy (by Mockito.spy(AbstractStepBuilder.class)) to avoid creating a subclass just for the test.

daniu
  • 14,137
  • 4
  • 32
  • 53
  • @davidxxx Yeah but I was to lazy to find the section in the documentation advising against it :P Also, creating a subclass specifically for the test also has a code smell to me. So I guess it's a lesser of two evils situation. – daniu Mar 02 '20 at 14:11
  • Create a subclass for the test is not necessarily the best thing today either , I agree :) Maybe a path between them is possible :) – davidxxx Mar 02 '20 at 14:15
  • @davidxxx Maybe define a subclass and then create a spy of that... ;) – daniu Mar 02 '20 at 14:16
  • Is it not the worst of the two worlds ? No I think that you first idea is fine. Usually I don't try to test abstract classes because that is abstract. If I really need to do that because for example that is a class that I expose to other clients, creating a subclass in a test scope is not really a bad smell. Whatever you have my vote for the first point :) – davidxxx Mar 02 '20 at 14:24
  • 1
    @davidxxx yes, that was a joke. – daniu Mar 02 '20 at 14:34
  • I supposed that but not sure :) – davidxxx Mar 02 '20 at 14:35