16
import org.springframework.beans.factory.annotation.Autowired;

class MyService {
  @Autowired private DependencyOne dependencyOne;
  @Autowired private DependencyTwo dependencyTwo;

  public void doSomething(){
    //Does something with dependencies
  }
}

When testing this class, I basically have four ways to inject mock dependencies:

  1. Use Spring's ReflectionTestUtils in the test to inject the dependencies
  2. Add a constructor to MyService
  3. Add setter methods to MyService
  4. Relax the dependency visibility to package-protected and set the fields directly

Which is the best and why?

--- UPDATE ---

I guess I should have been a bit clearer - I am only talking about "unit" style tests, not Spring "integration" style tests where dependencies can be wired in using a Spring context.

Daniel Alexiuc
  • 13,078
  • 9
  • 58
  • 73

3 Answers3

20

Use ReflectionTestUtils or put a setter. Either is fine. Adding constructors can have side effects (disallow subclassing by CGLIB for example), and relaxing the visibility just for the sake of testing is not a good approach.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 2
    That's usually what I end up doing as well (ReflectionTestUtils). In my opinion, this in one of the few major weaknesses in Spring, the lack of support for easily replacing/mocking beans directly into the context. – pap Jul 27 '11 at 07:47
  • "this in one of the few major weaknesses in Spring, the lack of support for easily replacing/mocking beans directly into the context" - but there is no Spring context in his unit test. He is simply `new`ing the MyService class. – matt b Jul 28 '11 at 02:50
  • @matt b - it doesn't matter - the question is about the possibilities of setting dependencies. – Bozho Jul 28 '11 at 05:32
  • 1
    Setters are ugly because they are only used by the tests, and RefelectionTestUtils is ugly because it doesn't keep up with field name refactorings. Which is less ugly? – Daniel Alexiuc Aug 01 '11 at 04:08
  • 2
    setters :) because they are the normal way of setting dependencies. Spring uses field reflection to do that, but it could've been a using setter reflection (bean properties) – Bozho Aug 01 '11 at 05:09
3

Spring's ContextConfiguration can do this for you.

For example, in the test context below the "Local" classes are mocks. NotificationService is the class I want to test.

I am using component scanning to bring the mocks into the context but you can just as easily use <bean> declarations. Note the use of use-default-filters="false".

<context:component-scan base-package="com.foo.config" use-default-filters="false">
    <context:include-filter type="assignable" 
        expression="com.foo.LocalNotificationConfig"/>
</context:component-scan>

<context:component-scan base-package="com.foo.services.notification"
        use-default-filters="false">
    <context:include-filter type="assignable"
        expression="com.foo.services.notification.DelegatingTemplateService"/>
    <context:include-filter type="assignable"
        expression="com.foo.services.notification.NotificationService"/>
</context:component-scan>

<context:component-scan base-package="com.foo.domain"/>

DelegatingTemplateService is a Groovy class with a @Delegate.

class DelegatingTemplateService {
  @Delegate
  TemplateService delegate
}

In the test class I use the test context and inject the service to test. In the setup I set the delegate of the DelegatingTemplateService:

@RunWith(classOf[SpringJUnit4ClassRunner])
@ContextConfiguration(Array("/spring-test-context.xml"))
class TestNotificationService extends JUnitSuite {
  @Autowired var notificationService: NotificationService = _
  @Autowired var templateService: DelegatingTemplateService = _

  @Before
  def setUp {
    templateService.delegate = /* Your dynamic mock here */
  }  

In the service the @Autowired fields are private:

@Component("notificationService")
class NotificationServiceImpl extends NotificationService {
  @Autowired private var domainManager: DomainManager = _
  @Autowired private var templateService: TemplateService = _
  @Autowired private var notificationConfig: NotificationConfig = _
sourcedelica
  • 23,940
  • 7
  • 66
  • 74
  • 1
    yes, you can swap implementations for test and production. But I had in mind mocks generated at runtime with something like Mockito, hence my answer. – Bozho Jul 27 '11 at 18:01
  • gotcha - how about using a Groovy @Delegate? See my updated answer. – sourcedelica Jul 27 '11 at 18:22
2

2) Use @Autowired constructor injection (if that's what option 2 was; otherwise, make an option 5)

Proper constructors that create an object in a valid state is a more correctly object-oriented approach, and losing cglib proxies is relatively unimportant since we all code to interfaces anyway, right??

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199