Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking an abstract class with a mocked constructor argument?

I'd like to use Mockito to unit test an abstract class as detailed in this great answer.

The trick is, the abstract class has a dependency on a strategy that gets injected in its constructor. I've created a mock of the strategy and I'd like for my mocked instance of BaseClass to use the mocked strategy for my unit test.

Any suggestion as to how I can wire this up? I'm not currently using any IoC framework, but am considering Spring. Perhaps it would do the trick?

// abstract class to be tested w/ mock instance
abstract BaseClass
{
    // Strategy gets mocked too 
    protected BaseClass( Strategy strategy)
    {
        ...
    }
}

Update:
According to the Mockito mailing list, there currently isn't a way to pass arguments to the constructor of a mock.

like image 906
HolySamosa Avatar asked Apr 16 '12 22:04

HolySamosa


1 Answers

I ended up just using reflection to set a private field in my base class, like so:

// mock the strategy dependency
Strategy strategyMock = mock( Strategy.class);
when(....).thenReturn(...);

// mock the abstract base class
BaseClass baseMock = mock(BaseClass.class, CALLS_REAL_METHODS);

// get the private streategy field
Field strategyField = baseMock.getClass().getSuperclass().getDeclaredField("_privateStrategy");

// make remove final modifier and make field accessible
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(strategyField, strategyField.getModifiers() & ~Modifier.FINAL);          
strategyField.setAccessible(true);

// set the strategy
strategyField.set(baseMock, strategyMock);

// do unit tests with baseMock
...

It would break if the name of the private field ever changed, but its commented and I can live with that. It's simple, it;s one line of code and I find this preferable to exposing any setters or having to explicitly subclass in my tests.

Edit: So it's not one line of code anymore since my private field needed to be 'final', requiring a some extra reflection code to get around.

like image 188
HolySamosa Avatar answered Sep 20 '22 21:09

HolySamosa