mocking abstract classes

11,972

Solution 1

When I want to unit test an Abstract class I don't mock, I subclass.

borrowing code from mijer in other answer

public class MockitoTest {
    public static abstract class MyAbstractClass {
       private int state;
       public abstract int abstractMethod();

       public int method(....)
       {
        ...
       }
    }
}


class Testclass extends MyAbstractClass 
{
      public int abstractMethod()
      {
       ...
      }
 }

Then run your tests of MyAbstractClass using an instance of Testclass. you can control the implementation of the abstract methods in your local subclass.

Solution 2

import org.junit.Test;
import org.mockito.internal.stubbing.answers.CallsRealMethods;

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class MockitoTest {
    public static abstract class MyAbstractClass {
        private int state;
        public abstract int abstractMethod();
        public void method() {
            System.out.println("method. State: " + (++state));
            System.out.println("abstractMethod: " + abstractMethod());
            anotherMethod();
        }
        public void anotherMethod() {
            System.out.println("anotherMethod. State: " + (++state));
        }
    }

    @Test
    public void test() throws Exception {
        MyAbstractClass obj = mock(MyAbstractClass.class, new CallsRealMethods());
        doReturn(5).when(obj).abstractMethod();

        obj.method();

        verify(obj).abstractMethod();

        assertEquals(2, obj.state);
    }
}

-EDIT-

  1. If you need to maintain internal state of the object you have to use org.mockito.internal.util.reflection.Whitebox.setInternalState, for example:

    @Test
    public void test() throws Exception {
        MyAbstractClass obj = mock(MyAbstractClass.class, new CallsRealMethods());
        setInternalState(obj, "state", 100);
        doReturn(5).when(obj).abstractMethod();
    
        obj.method();
    
        verify(obj).abstractMethod();
        assertEquals(102, obj.state);
    }
    
  2. If you have an abstract class with a complex logic in its constructor which you would like to test, you should extend this class just for testing or refactor your class moving all the logic to some method to be tested.

Share:
11,972
michael nesterenko
Author by

michael nesterenko

Exploring this world....

Updated on June 04, 2022

Comments

  • michael nesterenko
    michael nesterenko almost 2 years

    Possible Duplicate:
    Using Mockito to test abstract classes

    I have an abstract class with functionality I need to test. I could create simple derivative of that class with no op implementations of abstract methods, but is it possible to be done with mocking framework? I need to maintain class internal state, so I can't just call

    mockedInstance = mock(ClassUnderTest.class);
    

    I need something

    mockedInstance = spy(new ClassUnderTest(...));
    

    but apparently this is impossible to do as class is abstract.

  • michael nesterenko
    michael nesterenko over 12 years
    What if I need to pass parameters to a constructor?
  • michael nesterenko
    michael nesterenko over 12 years
    intent is not to extend a class but use a mocking framework
  • michael nesterenko
    michael nesterenko over 12 years
    That is a bit cumbersome to set internal state this way. In real examples there could be much more variables, complex logic etc.
  • szhem
    szhem over 12 years
    This means you should simplify/refactor your code somehow to be more easy-testable and understandable. Moreover, it's a good practice. Why do you need to call a constructor instead of testing class methods?
  • michael nesterenko
    michael nesterenko over 12 years
    I have initialization logic in constructor that needs to be executed before to be able to test methods.
  • szhem
    szhem over 12 years
    In that case you have to test real classes instead of abstract ones. I suppose it's hardly possible to extend an abstract class, to generate all corresponding constructors in the generated class, to add calls to constructors of super class, and then implement all abstract methods with current mocking frameworks.
  • Don Branson
    Don Branson over 10 years
    This approach is to be avoided, since you're no longer testing the class, but the subclass, and may mask or introduce bugs. It also fails to scale well as tests are added to the test fixture.