Look Mate, No Setters, No XML!

Lately I had a discussion about setters that have been introduced for the sake of testability. Just to be able to replace the actual collaborator with a stub or mock.

2104233 801fe7669e q Look Mate, No Setters, No XML!

The discussion went something like this:

S: “We could use a dependency injection in conjunction with the @Inject annotation with a container like Spring to avoid introducing protected setters for tests only.”

H: “But, isn’t that a lot of XML? Furthermore, you have to look in several places to get the overall picture of the test: The Java source code and the XML file.”

S: “There’s a very persistent myth that whenever you use Spring, you are doomed to live with almost as many XML files as code. By now it should be possible without a single line of XML…”

H: “Ok. Let’s give it a try!”

First we did some software archaeology[1] work …looking around in some source repositories, we found a plausible cause of how the myth came about. Once upon a time there was a Hero with a setter introduced for the sole purpose of testing:

public class Hero {
  private Sword sword;
 
  public Hero() {
    this(new DragonSlayer());
  }
 
  // for testing only
  void setSword(Sword sword) {
    this.sword = sword;
  }
 
  public void fight() {
    this.sword.swing();
  }
}

And, as expected, the XML file and

<beans xmlns="...spring-beans.xsd">
 
	<bean id="hero" class="com.eclipsesource.examples.junit.spring.xml.Hero" />
 
</beans>

the actual test code:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "HeroTest-context.xml" })
public class HeroTests {
 
    @Autowired
    private Hero uut;
 
    @Test
    public void shouldSwingSwordWhenFighting() {
        Sword sword = createMock(Sword.class);
        uut.setSword(sword);
        sword.swing();
 
        replay(sword);
 
        uut.fight();
 
        verify(sword);
    }
 
}

Ok. So far so good. But is this code necessary any more in times of the Java-based container configuration[2] available in Spring?

Let’s rewrite the Hero to use dependency injection as defined in JSR 330[3]:

public class Hero {
 
    @Inject
    private Sword sword;
 
    public void fight() {
        this.sword.swing();
    }
}

And convert the XML into its annotation-based counterpart

    @Configuration
    static class ContextConfiguration {
 
        @Bean
        public Sword sword() {
            return createMock(Sword.class);
        }
 
        @Bean
        public Hero uut() {
            return new Hero();
        }
    }

Which can be easily integrated into the actual test class to have the configuration and the test in one place.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class HeroTests {
 
    @Configuration
    static class ContextConfiguration {...}
 
    @Inject
    private Hero uut;
 
    @Inject
    private Sword sword;
 
    @Test
    public void shouldSwingSwordWhenFighting() {
        sword.swing();
        replay(sword);
 
        uut.fight();
 
        verify(sword);
    }
 
}

H: Wow. That’s all we need in one place and not a single line of XML!

S: I’d say this one is “Busted”.

[1] Software Archeology – http://en.wikipedia.org/wiki/Software_archaeology
[2] Java-based container configuration – http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java
[3] JSR-330 Dependency Injection for JavaTM – http://jcp.org/aboutJava/communityprocess/final/jsr330/

Image “Hero of Time” – from http://www.flickr.com/photos/alisdair/2104233/

One Response to “Look Mate, No Setters, No XML!”

  1. markus says:

    adam bien suggests to use package-private properties and tests which live in the same pacakge than implementation to get arround the problem.

    In my eyes using package-private is even better then the mentioned approach in this post, because it works with java out of the box, without any framework dependencies (and also with less code)

One response so far

Written by . Published in Categories: EclipseSource News, Editors choice