Search code examples
unit-testingabstract-classmockitoparentlegacy

Mock Abstract Parent Method of Field


FIRSTLY: I am testing legacy code.

I am testing an abstract class we will call Class A.

Class A has Field of type Class B. Class B is a child of Abstract base class we will call B_Base.

MY PROBLEM: In a method of class A, the instance of class B calls a method in B_Base.

I have tried mocking a B class and then replacing the Field with the mocked B Class, however, everytime it runs the parent method, there is a NullPointerException in the Mockito.when() method, meaning the parent B_Base class is NOT mocked.

Here is an idea of what the code looks like.

public abstract class A {     //this is the class Im testing.
      public B bInstance;

      public boolean methodIAmTesting {
           int hello = bInstance.method_Only_In_B_Base();
           ...
      }    
}

The test:

 public class TestClass extends A {
    //here is the constructor etc..

    @Test
    public void test1 {   
        TestClass x = new TestClass();
        bInstance = Mockito.mock(B.class);

        // ERROR IS HERE V
        Mockito.when(bInstance.method_Only_In_B_Base()).thenReturn(50);
        Assert.assertTrue(x.methodIAmTesting());
    }
 }

Solution

  • As J L mentioned, you've actually got two instances kicking around, because your TestClass extends A.

    What you have:

    public class TestClass extends A {
      @Test public void test1() {
        Class TestClass x = new TestClass();
        bInstance = Mockito.mock(B.class);
        // ...
      }
    }
    

    What you want:

    public class TestClass extends A {
      @Test public void test1 {
        Class TestClass x = new TestClass();
        x.bInstance = Mockito.mock(B.class);  // <-- replace bInstance on x
        // ...
      }
    }
    

    JUnit creates an instance of your test class automatically, so when you call x = new TestClass() you're creating a separate instance for test, and your call for bInstance = mock(B.class) is setting the mock on the instance that JUnit creates rather than the x instance you create.

    Note that it's very unusual to have a JUnit test class subclass the system under test. Consider a design like this instead:

    @RunWith(JUnit4.class)
    public class ATest {
      private static class TestClass extends A {
        // ...
      }
    
      @Test public void test1 {
        Class TestClass x = new TestClass();
        x.bInstance = Mockito.mock(B.class);
        // ...
      }
    }