Re: Creating a partial mock from an abstract class with injected fields

42 views
Skip to first unread message

Stephan Classen

unread,
May 2, 2014, 3:04:33 AM5/2/14
to juk...@googlegroups.com

I did replace the helper class by a mock for testing and tested that the helper mock was invoked. So yes I tested several times in different classes that the helper was invoked but this test is much simpler than testing the logic of the helper. Also when the behavior of the helper needs to change I have only one test to change. Since all other tests only assert the delegation.
This is the pure unit test approach. In a more integration test centered approach I would proabably not even write a test for the delegation since I try to cover as much logic by the integration test as possible in favor of testing every little method by a unittest. But finding the right balance between unit tests and integration tests is hard and I still haven't found it for my self.

Am 02.05.2014 08:39 schrieb Anton Beloglazov <anton.be...@gmail.com>:
Hi Stephan,

Thanks for your recommendations! When you moved the implementation from the abstract class to the helper class, did you avoid testing the methods that delegate calls to the helper class in the classes implementing the interface? Otherwise, it seems that testing the delegating methods also leads to duplicated tests, as the helper class was probably tested independently as well.

Thanks,
Anton

On Thursday, 1 May 2014 16:39:24 UTC+10, scl wrote:

I had the same idea when I started with TDD and stumbled over an abstract class.
I didn't use mockito but wrote my own concrete subclass which implemented all abstract methods by doing nothing...

In the end I had a bunch of duplicated tests. Since I tested the behavior of the abstract class through my special subclass and the productive subclasses. Some of the productive sub classes altered the behavior of the base class slightly. So  I could not declare the method in the abstract class as final.

In the end I changed my abstract class to be an interface and moved all implementation which previously was in the abstract class to a new helper class. In the subclasses of my new interface I delegated the calls to the new helper class.
Using delegation I could test the common behavior in the helper class. And I could test which implementation of the interface relay on the default behavior and which defined there own logic.

Am 01.05.2014 07:49 schrieb Anton Beloglazov <anton.be...@gmail.com>:
Hi Stephan,

You suggestion works perfectly, thanks a lot! :) I'm going to test the subclasses nevertheless, but since I have some logic in the abstract class, I wanted to test that as well. 

Cheers,
Anton


On Wed, Apr 30, 2014 at 8:41 PM, Stephan Classen <st.cl...@gmx.ch> wrote:

Ok first thing:
You should not test abstract classes but the concrete class which extends your base class. You can use google to find various reasons for this. The most obvious one is, that a subclass may change the behavior of a super class and therefore break the contract you have tested.

Never the less here is how you can do it:
Take your second approach and change the before method to

@Before
public void setupMocks(Injector injector) throws SQLException {
when(dataSource.getConnection()).thenReturn(connection);

holder = mock(AbstractConnectionHolder.class, CALLS_REAL_METHODS);

injector.injectMembers(holder);
}

This should inject the dependencies into your mock.

Am 30.04.2014 10:33 schrieb Anton Beloglazov <anton.be...@gmail.com>:
Hi All,

I'm trying to test an abstract class that has some injected fields. Basically, the class is the following:

public abstract class AbstractConnectionHolder {

@Inject
private Configuration configuration;

@Inject
private ConnectionContext context;

protected ConnectionContext getConnectionContext() {
return context;
}

protected Configuration getConfiguration() {
return configuration;
}

public Connection openConnection() {
return getConnectionContext().openConnection();
}

public void closeConnection() {
getConnectionContext().closeConnection();
}

public Connection getConnection() {
return getConnectionContext().getConnection();
}

}

To test it, I'm trying to create a new partial mock for every test. I've managed to run the following:

@RunWith(JukitoRunner.class)
public class AbstractConnectionHolderTest {

@Inject
DataSource dataSource;

@Inject
Connection connection;

@Inject
Configuration configuration;

@Inject
ConnectionContext context;

public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bind(Configuration.class).toInstance(new Configuration(new PostGISTemplates()));
bind(AbstractConnectionHolder.class).toInstance(
mock(AbstractConnectionHolder.class, CALLS_REAL_METHODS));
}
}

@Before
public void setupMocks() throws SQLException {
when(dataSource.getConnection()).thenReturn(connection);
}

@Test
public void shouldReturnConfiguration(AbstractConnectionHolder holder) {
assertThat(holder.getConfiguration(), is(configuration));
}

}

This runs, but it only allows me to have a single AbstractConnectionHolder mock for all tests. The ideal case would be to make it somehow inject a new AbstractConnectionHolder partial mock into each test, which I couldn't figure out how to do. Is this possible?

I also tried to manually create mocks in the @Before method as follows:

@RunWith(JukitoRunner.class)
public class AbstractConnectionHolderTest {

@Inject
DataSource dataSource;

@Inject
Connection connection;

@Inject
Configuration configuration;

@Inject
ConnectionContext context;

AbstractConnectionHolder holder;

public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bind(Configuration.class).toInstance(new Configuration(new PostGISTemplates()));
}
}

@Before
public void setupMocks() throws SQLException {
when(dataSource.getConnection()).thenReturn(connection);
holder = mock(AbstractConnectionHolder.class, CALLS_REAL_METHODS);
}

@Test
public void shouldReturnConfiguration() {
assertThat(holder.getConfiguration(), is(configuration));
}

}

However, this fails with the following exception:

java.lang.AssertionError: 
Expected: is <com.mysema.query.sql.Configuration@7a79dbc7>
     but: was null
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at test.AbstractConnectionHolderTest.shouldReturnConfiguration(AbstractConnectionHolderTest.java:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.jukito.InjectedStatement.evaluate(InjectedStatement.java:96)
at org.jukito.InjectedBeforeStatements.evaluate(InjectedBeforeStatements.java:51)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.jukito.JukitoRunner.run(JukitoRunner.java:187)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)


As I understand, for some reason in this case it wasn't able to inject the fields of AbstractConnectionHolder. Is there a way to solve the original problem or this injection issue?

Thanks a lot!

Best regards,
Anton

--
You received this message because you are subscribed to the Google Groups "Jukito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jukito+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Jukito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jukito+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Jukito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jukito+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Anton Beloglazov

unread,
May 4, 2014, 9:48:59 PM5/4/14
to juk...@googlegroups.com
Hi Stephan,

Thanks a lot for the explanation! Now I understand the purpose of extracting a helper class. In my case, I'd like to make child classes of the abstract class inherit the implementation and have it accessible as instance methods without any extra work. This would help to avoid creating delegation methods in each subclass if they don't override the behavior of the parent class. To do that I have to keep some implementation in the abstract class, and therefore, test it. I need to experiment more to better understand the balance between unit and integration testing.

Cheers,
Anton

Stephan Classen

unread,
May 5, 2014, 1:27:18 AM5/5/14
to juk...@googlegroups.com
Happy experimenting :)

Creating delegation methods is something I delegate to my IDE.
So I don't worry about them, they come for free at code time and almost for free at runtime (the JIT-compiler optimizes them pretty well). Nowadays I tend to favor delegation over inheritance.
But it is to some point a gusto what you prefer.

Here some rules when you should use delegation instead of inheritance:
http://developergeeks.com/article/16/when-not-to-use-inheritance-but-delegation

For more background and opinions I recommend to ask the Internet.

Anton Beloglazov

unread,
May 6, 2014, 5:44:17 AM5/6/14
to juk...@googlegroups.com
Thanks for your advice and the link, it was useful to read!

Best regards,
Anton
...
Reply all
Reply to author
Forward
0 new messages