user1639485
user1639485

Reputation: 808

Mocking a non-abstract method of an abstract class

I am trying to unit-test a class that extends an abstract base class. Here are the "similar classes" for illustration purposes:

public abstract class MyAbstractBaseClass {
  @Autowired
  private WaterFilter waterFilter;

  protected List<String> filterComponents(List<String> allComponents) {
    return waterFilter.filter(allComponents);
  }
}

public class MyDerivedClass extends MyAbstractBaseClass {
  public List<String> filterWater(List<String> allWaterComponents) {
    List<String> filteredComponents = this.filterComponents(allWaterComponents); //calls abstract class's filterComponets()
    filteredComponents.add("something-else");
    return filteredComponents;
  }
}

Here is the unit test I am trying:

    @RunWith(EasyMockRunner.class)
    public class MyDerivedClassTest {
        @TestSubject
        private MyDerivedClassTest SUT;

        @Before
        public void setup() {
           SUT = new MyDerivedClassTest();
        }

        @Test
        public void test filterWater_HappyCase() {
           //I want to mock my abstract class's filterComponents() method
           //I am trying this:
           EasyMock.expect(SUT.filterComponents(getDummyComponents())).andReturn(getSomeComponents());

          //What to replay here?
          //EasyMock.replay(...)

          List<String> actualResult = SUT.filterWater(getDummyComponents());

          //assert something
          //What to verify?
          //EasyMock.verify(...)
    }
}

When I run this test, I get

java.lang.NullPointerException

in MyAbstractBaseClass.filter(allComponents)

I understand that the autowired "waterFilter" is not getting initialized. But then, I just want to mock the "non-abstract" method of the abstract class in my unit test.

How should I go about this using EasyMock? Also, I don't know what to replay() and verify().

Upvotes: 2

Views: 1872

Answers (2)

davidxxx
davidxxx

Reputation: 131376

When you write an unit test, you test a object (generally, a method of it) and you may mock a object (generally, a method of it).
However, you should not unit test and mock the same object because in a some way, it seems not very natural : if you test a method of a class, the behavior of the tested class should stay as natural as possible and not fake its own methods.
Otherwise, we can wonder if the quality of the unit test is good.
Why ? Because it doesn't reflect the real behavior of the class that we would have at the runtime, but just some part of its behavior. In a unit test, isolation is researched but behind it, the idea is to isolate your under test class only from the other classes and not an isolation of its own behavior.
Of course, you can try to mock a no-abstract method in the abstract class of your under tested class but the design and the quality of your test may become less good.

In your case, I imagine two reasons to mock the no-abstract method in the abstract class :

  • the waterFilter field dependency annoys you because it is not valued, so an exception (NullPointerException) is raised during the test.
  • You really want to mock the no abstract method in the abstract class because you have already unitary tested this method and you don't want to duplicate this test.

1) If your the problem is the waterFilter field dependency.

you should mock the waterFilter field. To mock a field, it must be accessible and modifiable. In your case, it's not straight because the field is private.

So, you have two ways to access to it to be able to mock it :

  • change your design to give the possibility to set the field from a public method or in the constructor of MyDerivedClass.
  • use reflection to set the field (use an API or do it yourself because it's not hard).

You don't need to do verify operations with EasyMock. Just mock the result returned by waterFilter.filter(allComponents) such as :

 waterFilterMock.filter(mockedComponents) 

In this way, the mock returns the value you have chosen and in your JUnit assertion, you are able to do the correct assertion for your method under test.

Just for information, you could use Mockito instead of EasyMock. It's more flexible and it offers more readable operations. For example, you could do it with Mockito :

Mockito.when(waterFilterMock.filter()).thenReturn(mockedComponents);

As you can see, it is more readable.

2) If you problem is that you really want to mock the no-abstract method in the abstract class because you have already unitary tested it

You should modify your design and use composition instead of inheritance. You would have not MyAbstractBaseClass any longer but simply a dependency between two classes (the one has a field of the other). In this way, you could mock the filterComponents() method in a natural way.

Upvotes: 3

Sumit Kumar
Sumit Kumar

Reputation: 395

Expectations has to be set on mocked resources. In your case i think you should inject a mocked instance of WaterFilter.

And your expectation,replay and verify should be set on waterFilter object instance.

You can refer to the example provided in given below link. http://www.tutorialspoint.com/easymock/easymock_junit_integration.htm

Upvotes: 0

Related Questions