Question

I have previously used Spring DI, and one of the benefits I perceive is that I can test my Spring bean classes without involving Spring (imports omitted for brevity):

public class Foo {
    private String field;

    public void setField(String field) { this.field = field; }
    public String getField() { return field; }
} 

public class TestFoo {
    @Test
    public void test_field_is_set() {
       Foo foo = new Foo();
       foo.setField("Bar");
       assertEquals("Bar", foo.getField());
    }
}

Now I am experimenting with JSR-330, which means not explicitly writing setters.

I'm using Hk2 so far, purely because of some anecdotal stuff about Jersey being tied to Hk2, and making it difficult to co-habit with other JSR-330 implementations.

public class Foo {
   @Inject 
   private String field;
}

I half expected some magic to happen, whereby the @Inject annotation caused a setter to become available, but this is not the case:

Foo foo = new Foo();
foo.setField("Bar"); // method setField(String) is undefined for the type Foo
  • How can I (conveniently) test this kind of annotated class without invoking a framework?
  • Failing that, how can I invoke a framework in a portable way (i.e. without tightly coupling my test code to Hk2, Guice, etc.)
  • Failing that, what's a typical, clean way to test classes annotated in this way?
Was it helpful?

Solution 3

It turns out that frameworks relying on private/protected field access are not so uncommon. Hibernate, JPA, several JSR-330 implementations, including Spring itself, all do it.

Spring's spring-test package provides a ReflectionTestUtils class containing static methods for accessing these fields.

Using this one can test the class in the question thus:

import static org.springframework.test.util.ReflectionTestUtils.*;

...

@Test
public void testUsingSpringReflectionTestUtils() {
    Foo foo = new Foo();
    setField(foo, "field", "Bar");
    assertEquals("Bar", foo.getField());
}

You need spring-test and spring-core in your test classpath for this to work, but it doesn't add a dependency on Spring for your production code.

(Comments welcome about alternative implementations of the same principle welcome. I don't think it's worth rolling one's own, however simple it would be, given that Spring has a good implementation.)

OTHER TIPS

Simplest is to make the fields package-private (instead of private), then in the test, set them directly. (That works if the test is in the same package)

public class Foo {
   @Inject 
   String field;
}



Foo foo = new Foo();
foo.field = "bar";

This has the advantage of avoiding reflection so it's safe for refactoring.

The field injection approach you mentioned is actually the typical Spring style; many programmers don't write setters for private injected fields at all. Spring (with @Autowired or @Inject) and JSR-330 containers usually inject fields using direct field reflection rather than setters.

Because of this, if you don't want to use any DI framework, you could write the necessary reflection code into your unit tests yourself, but this seems like overkill just to avoid a test dependency; after all, the point of using @Inject is that you're coding to an interface, and you don't avoid using the JVM to avoid coupling to it.

The usual approach for testing this sort of class is to set up a test context for whatever container you prefer and run the unit tests in that context. If you're using Spring, you'd put an applicationContext-test.xml file or TestConfig class in your src/test/ directory (or equivalent), and if you're using Guice, you'd write a module to wire up mocks or test datasets.

Give "needle" a try: http://needle.spree.de/overview

needle is an DI-test-framework that only simulates the container behavior, making unit tests real simple.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top