I'm running into some issues with how I can test this code correctly. I want to be able to mock out the call to delegateForFoo either using a Mocking framework, or even though reflection. However, when I try to do this through reflection, or through PowerMock, I get the errors as shown in the code below.
@Test
public void testDelegatedMethodWithPowerMock() throws Exception {
DelegateForFoo mockedDelegateForFoo = PowerMock
.createMock(DelegateForFoo.class);
Foo foo = Whitebox.invokeConstructor(Foo.class, mockedDelegateForFoo);
// org.powermock.reflect.exceptions.ConstructorNotFoundException: Failed
// to find a constructor with parameter types: at line above.
Constructor<Foo> fooConstructor = Foo.class
.getDeclaredConstructor(DelegateForFoo.class);
fooConstructor.setAccessible(true);
Foo foo2 = fooConstructor.newInstance(mockedDelegateForFoo);
// java.lang.InstantiationException at line above.
}
I did my best to check previous stackOverflow questions and I didn't see one that addresses this issue directly. There are some answers on how to call private constructors of concrete classes, and some on how to class private methods on abstract classes, but none on this particular instance.
My official question is as follows: Is there a way to call a private constructor of an abstract class and pass in a mocked object for testing purposes.
I know generally you shouldn't be testing private methods, etc. However in this case, I want to test a constructor that has a legitimate reason to be private. There is no reason for Bar to know anything about the delegate at all and for proper encapsulation, it should be private. Thanks in advance.
The code that I am using is below.
public abstract class Foo {
private DelegateForFoo delegateForFoo;
private int valueToBeSetFromDelegate;
Foo() {
// I don't want the subclasses to do anything with this DelegateForFoo.
// It's set through the constructor because there is a requirement that
// Foo must have a delegateForFoo in order to be created.
this(new DelegateForFoo());
}
// This Constructor is indirectly called by the subclasses. They don't know
// anything about how it is set, or what it does.
private Foo(DelegateForFoo delegateForFoo) {
this.delegateForFoo = delegateForFoo;
this.valueToBeSetFromDelegate = this.delegateForFoo.delegatedMethod();
}
int useTheDelegateForSomeMethod() {
return this.delegateForFoo.delegatedMethod();
}
}
public class DelegateForFoo {
int delegatedMethod() {
return 5;
}
}
public class Bar extends Foo {
// here, Bar doesn't care about the delegate for Foo at all. It only cares
// that the method it is called has some implementation.
int useDelegateForFooWithoutKnowingAboutIt() {
return super.useTheDelegateForSomeMethod() + 10;
}
}
The reason why you can not create the instance of your Foo
class is because it is abstract
. There is no mechanism in Java that could create instance of an abstract class.
To test abstract class you need to define new class that will extend Foo. This mean that the default constructor will be invoked.
As other people have pointed out, you're not going to be able to do anything with the Foo
constructors class directly because of the abstract-ness of the class.
Having said that, you should be able to write a test for the Bar
class if you think about it in a slightly different manner. Instead of mocking the Foo
constructor calls, mock the DelegateForFoo
constructor call.
I think I'd do it like this:
So it would look something like this: (I apologise for any typos, I've not tried this in an IDE)
@RunWith( PowerMockRunner.class )
@PrepareForTest( Foo.class, Bar.class, DelegateForFoo.class )
public class BarTest
@Test
public void thatDelegateForFooCanBeMocked() {
DelegateForFoo mockDelegate = EasyMock.createMock(DelegateForFoo.class);
EasyMock.expect( mockDelegate.delegatedMethod() ).andReturn( 5 );
EasyMock.replay( mockDelegate );
PowerMock.expectNew( DelegateForFoo.class ).andReturn( mockDelegate );
PowerMock.replayAll();
Bar bar = new Bar();
int result = bar. useDelegateForFooWithoutKnowingAboutIt();
Assert.assertThat( result, CoreMatchers.is(15) );
PowerMock.verifyAll();
EasyMock.verify(mockDelegate);
}
}
Of course, documentation is a wonderful thing, so see the PowerMock: Mocking a Constructor documentation to find out more about constructor mocking with PowerMock.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With