Look Mate, No Setters, No XML!

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

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



	


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/

1 Comment
  • markus
    Posted at 3:21 pm, June 7, 2013

    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)