@Autowired + PowerMock: Fixing Some Spring Framework Misuse/Abuse
Unfortunately, I have seen it misused several times when a better design would be the solution.
Join the DZone community and get the full member experience.
Join For FreeEvery time I see PowerMock as a test dependency I tend to get nervous. Don't get me wrong; PowerMock is a great library and does great work. I just have a problem with people who abuse it. I don't doubt there are many scenarios where it will be very useful; however, I have yet to actually find one in my Real World(TM) job. Unfortunately, I have seen it misused several times when a better design would be the solution.
One way I have seen it used is with Spring @Autowired in private fields. Of all ways to use Spring DI, this is the worst. You cannot properly test; you depend on Spring to fully populate your class, and you cannot reuse your class without carrying the whole Spring framework.
I was once working on a project with lots of classes like this:
public class Client {
@Autowired
private Service service;
public void doSomething() {
service.doServiceStuff();
}
}
Being private fields, there is no correct way to set them outside Spring. You will need to use Reflection or some sort of black magic.
The developers opted to use PowerMock:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Client.class })
public class ClientTest {
@InjectMocks
private Client client= new Client();
@Mock
private Service service;
@Test
public void doSomethingTest {
//setup test data
client.doSomething();
}
}
They decided to add a new dependency and make the code even more complicated. I then refactored it like this:
public class Client {
private Service service;
@Autowired
public Client(final Service service) {
//check preconditions
this.service = service;
}
public void doSomething() {
service.doServiceStuff();
}
}
Now it is clear what this class depends on, and you cannot instantiate it (directly or indirectly) without the required dependencies (you will appreciate this when you have some nasty NPEs in production because someone added a new @Autowired private field). Not to mention we got rid of an unneeded dependency.
And the test looked like this:
@RunWith(MockitoJUnitRunner.class)
public class ClientTest {
@Mock
private Service service;
@Test
public void doSomethingTest {
//setup test data
final Client client= new Client(service);
client.doSomething();
//assertions and verifications
}
}
It is unfortunate that frameworks like Spring, otherwise very useful, are so easily misused (should I say abused?) by people who don't take the time to analyze the problem at hand. Maybe a good framework should not be that powerful? Or maybe going the not preferred way should be tedious and effort consuming? I would like to know your opinions about this.
Soon I'll be posting more magic with PowerMock to fix a bad design.
Opinions expressed by DZone contributors are their own.
Comments