mardi 28 juin 2016

Mocking a generic class with an abstract version of said class

I'm attempting to mock an abstract class, but from what I've seen, I don't think it's possible. We have some classes that use generics, that must extends a specific abstract class. There's a whole group of them and they have been mocked successfully. The abstract class has one method that deals with returning the generic and looks like this:

public abstract class ChildPresenter <T extends ChildView> {
    private T view;

    public T getView() {
        return view;
    }
}

The class we are testing has the following in it:

public class ParentPresenter {
    private ConcreteChildPresenter1 childPresenter1;
    private ConcreteChildPresenter2 childPresenter2;
    private ConcreteChildPresenter3 childPresenter3;
    private ConcreteChildPresenter4 childPresenter4;
    List<ChildPresenter> childPresenters;
}

In the constructor, these classes are injected in, using Google Guice, set to the variables, and added to the list of child presenters.

The method under test is one that iterates over all of the childPresenters objects and runs the method getView().

I attempted it this way in my test class:

public class ParentPresenterTest {
    private ConcreteChildPresenter1 childPresenter1;
    private ConcreteChildPresenter2 childPresenter2;
    private ConcreteChildPresenter3 childPresenter3;
    private ConcreteChildPresenter4 childPresenter4;
    private List<ChildPresenter> childPresenters;

    //This is an abstract class 
    private ChildView childView;

    @BeforeTest
    public void createInjector() {
        Guice.createInjector(...//Creates a fake module and does binding for the variables mentioned earlier
            //e.g.
            childPresenter1 = mock(ConcreteChildPresenter1.class);
            binder.bind(ConcreteChildPresenter1.class).toInstance(childPresenter1);
            //e.t.c for other variables

            //Same for child view
            childView = mock(ChildView.class);
            binder.bind(ChildView.class).toInstance(childView);
        }

        childPresenters = new ArrayList<ChildPresenter>();
        childPresenters.add(childPresenter1);
        //Add all child presenters

        for(ChildPresenter childPresenter : childPresenters) {
            when(childPresenter.getView()).thenReturn(childView);
        }
    }
}

The problem happens at the line when(childPresenter.getView()).thenReturn(childView); as Mockito complains with the following message:

org.mockito.exceptions.misusing.WrongTypeOfReturnValue:

ChildView$$EnhancerByMockitoWithCGLIB$$2f6a4bd5

cannot be returned by getView() getView() should return ConcreteChildView1

*** If you're unsure why you're getting above error read on. Due to the nature of the syntax above problem might occur because:

  1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency testing.

  2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

Which I can understand, but it seems a waste to mock each individual concrete ChildView when all I want to do is confirm the mocked ChildView called a single method using the following:

verify(childView, atLeast(childPresenters.size())).getView();

Is there another way to do this? Can I somehow use mocked abstract classes in place of the concrete ones?

Aucun commentaire:

Enregistrer un commentaire