Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a call on a public method of an abstract class without subclassing the abstract Class, using mockito prefererably

I am writing an unit testing using JUNIT + Mockito to test a method like :

public someObject methodUnderTest(){
  SomeObject obj = SomeAbstractClass.someMethod();

  if(obj!=null){
    obj.someOtherMethod();
  }

  return someThing;
}

And I would like to mock the call on abstract Class "SomeAbstractClass" mentioned in above code fragment so i can verify call on "obj" like :

verify(SomeAbstractClass).someMethod();
verify(obj).someOtherMethod();

I have tried using mockito features like : Mockito.CALLS_REAL_METHODS Mockito.RETURNS_MOCKS

but they don't work due to dependencies not available to the SomeAbstractClass.

Note:

1) SomeObject is an Interface.

2) I need a technique to test above code fragment. I am constrained to use the above code fragment and cannot change the code fragment.

like image 981
Mrugen Deshmukh Avatar asked May 26 '12 10:05

Mrugen Deshmukh


People also ask

Can we mock abstract class using Mockito?

Spying abstract class using Mockito. spy() method is used to create a spy instance of the abstract class. Step 1: Create an abstract class named Abstract1_class that contains both abstract and non-abstract methods. Step 2: Create a JUnit test case named Abstract1Test. It contains a spy instance of the abstract class.

How do you inject mocks in abstract class?

Then you can use the @Mock , @InjectMocks as you would normally. If you have an implemented class which extends the Abstract class, then just use that one to test the methods from the abstract class.

Can we write JUnit for abstract class?

With JUnit, you can write a test class for any source class in your Java project. Even abstract classes, which, as you know, can't be instantiated, but may have constructors for the benefit of “concrete” subclasses.

Can abstract class have public abstract methods?

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.


4 Answers

You can use PowerMock to mock static and final methods.

like image 88
Garrett Hall Avatar answered Oct 05 '22 07:10

Garrett Hall


It sounds like the problem is that your use of CALLS_REAL_METHODS is applying to the entire class, where you really want to mock out specific methods (i.e. make a "partial mock"). You have two options here, one using thenCallRealMethod, and one using CALLS_REAL_METHODS and then specifically mocking the calls you need:

public void testMethodUnderTest_mockSpecificThings() {
    SomeAbstractClass myAbstractClass = Mockito.mock(SomeAbstractClass.class);
    SomeAbstractClass myObject = Mockito.mock(SomeObject.class);
    when(myAbstractClass.someMethod()).thenReturn(foo);
    when(myAbstractClass.methodUnderTest()).thenCallRealMethod();

    myAbstractClass.methodUnderTest();

    verify(myAbstractClass).someMethod();
    verify(myObject).someOtherMethod();
}

public void testMethodUnderTest_makeSpecificRealCalls() {
    SomeAbstractClass myAbstractClass =
        Mockito.mock(SomeAbstractClass.class, CALLS_REAL_METHODS);
    SomeAbstractClass myObject = Mockito.mock(SomeObject.class);
    // overrides the default answer
    when(myAbstractClass.someMethod()).thenReturn(myObject);

    myAbstractClass.methodUnderTest();

    verify(myAbstractClass).someMethod();
    verify(myObject).someOtherMethod();
}

Be forewarned that SomeAbstractClass is never actually instantiated, so if you rely on any behavior in the abstract class constructor, like variable initialization--including inline initialization where the fields are declared--you will need to make those calls explicitly yourself.

like image 20
Jeff Bowman Avatar answered Oct 05 '22 08:10

Jeff Bowman


Assumption: if you write unit test, I guess you still can modify tested method a bit.

Solution:

  1. extract static method call to overridable method:
public someObject methodUnderTest() {
    SomeObject obj = getSomeObject();

    if(obj!=null){
      obj.someOtherMethod();
    }

    return someThing;
}

protected SomeObject getSomeObject() {
    return SomeAbstractClass.someMethod();
}
  1. then you can use Mockito Spy to partially mock the object you actually test:
private ClassUnderTest classUnderTest;

@Before
public void setUp() {
    classUnderTest= new ClassUnderTest();
    classUnderTest = Mockito.spy(classUnderTest);
}

@Test
public void test() {
    SomeObject someObject = Mockito.mock(SomeObject.class);
    when(classUnderTest.getSomeObject()).thenReturn(someObject);
    classUnderTest.methodUnderTest();
    verify(someObject).someOtherMethod();
}

@Test
public void testNull() {
    when(classUnderTest.getSomeObject()).thenReturn(null);
    classUnderTest.methodUnderTest();
    verify(something);
}
like image 33
volkovs Avatar answered Oct 05 '22 08:10

volkovs


Use anonymous classes:

public interface SomeObject {
     public Object someOtherMethod();
}

public abstract class SomeAbstractClass {
    abstract SomeObject someMethod();
}

@Test
public void test() {
    SomeAbstractClass target = new SomeAbstractClass() {
        SomeObject someMethod() {
            // some impl
            SomeObject someObject = new SomeObject() {
                public Object someOtherMethod() {
                    // some other impl
                }
            };
            return someObject;
        }
    };

    // now test target
}
like image 20
Bohemian Avatar answered Oct 05 '22 08:10

Bohemian