About Damien Lepage

Damien is a software developer at Kijiji.ca, the leading classifieds web site in Canada and a branch of eBay classifieds. He's been working mostly with Java but is getting more and more interested in emerging languages on the JVM, especially Clojure.

Spring: To autowire or not to autowire

Since using Spring 2.5, I switched from the XML-based application context to the annotations. Although I find those very useful and huge time savers, I’ve always had the feeling that I was losing something in term of flexibility. In particular the @Autowired annotation – or the standard @Inject – felt to me like the new “new”, increasing the coupling between my classes and making it harder to change implementation when needed. I still feel that way a bit, but I’ve learned an interesting pattern to limit the problem when it comes to testing my code, i.e. when I want to replace the real implementation of a bean for a mock. Let’s illustrate with an example. I want to build an application to find interesting stuff on the web for me. I will start with a service which takes a URL and bookmarks it when if it’s a new one which happens to be interesting. Until recently, I may have coded something like this:

@Named
public class AwesomenessFinder {

  @Inject
  private BlogAnalyzer blogAnalyzer;

  @Inject
  private BookmarkService bookmarkService;

  public void checkBlog(String url) {
    if (!bookmarkService.contains(url) && blogAnalyzer.isInteresting(url)) {
      bookmarkService.bookmark(url);
    }
  }
}

This is bad, can you see why? If not, keep reading, I hope you will learn something useful today. Because I’m conscientious, I want to create unit tests for this code. Hopefully my algorithm is fine but I want to make sure it won’t bookmark boring blogs or bookmark the same URL twice. That’s where the problems appear, I want to isolate the AwesomenessFinder from its dependencies. If I was using an XML configuration, I could simply inject a mock implementation in my test context, can I do it with the annotations? Well, yes! There’s a way, with the @Primary annotation. Let’s try creating mock implementations for BlogAnalyzer and BookmarkService.

@Named
@Primary
public class BlogAnalyzerMock implements BlogAnalyzer {

  public boolean isInteresting(String url) {
    return true;
  }
}

@Named
@Primary
public class BookmarkServiceMock implements BookmarkService {

  Set bookmarks = new HashSet();

  public boolean contains(String url) {
    return bookmarks.contains(url);
  }

  public void bookmark(String url) {
    bookmarks.add(url);
  }
}

Because I use Maven and I put those mocks in the test/java directory, the main application won’t see them and will inject the real implementations. On the other hand, the unit tests will see 2 implementations. The @Primary is required to prevent an exception like:

org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [service.BlogAnalyzer] is defined: expected single matching bean
but found 2: [blogAnalyzerMock, blogAnalyzerImpl]

Now, I can test my algorithm:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application-context.xml")
public class AwesomenessFinderTest {

  @Inject
  private AwesomenessFinder awesomenessFinder;

  @Inject
  private BookmarkService bookmarkService;

  @Test
  public void checkInterestingBlog_bookmarked() {
    String url = "http://www.javaspecialists.eu";
    assertFalse(bookmarkService.contains(url));
    awesomenessFinder.checkBlog(url);
    assertTrue(bookmarkService.contains(url));
  }
}

Not bad, I tested the happy path, an interesting blog gets bookmarked. Now how do I go about testing the other cases. Of course I can add some logic in my mocks to find certain URLs already bookmarked or not interesting, but it would become clunky. And this is a very simple algorithm, imagine how bad it would get to test something more complex. There is a better way which requires to redesign my class and the way the dependencies are injected. Here is how:

@Named
public class AwesomenessFinder {

  private BlogAnalyzer blogAnalyzer;

  private BookmarkService bookmarkService;

  @Inject
  public AwesomenessFinder(BlogAnalyzer blogAnalyzer, BookmarkService bookmarkService) {
    this.blogAnalyzer = blogAnalyzer;
    this.bookmarkService = bookmarkService;
  }

  public void checkBlog(String url) {
    if (!bookmarkService.contains(url) && blogAnalyzer.isInteresting(url)) {
      bookmarkService.bookmark(url);
    }
  }
}

Note that I still autowire my dependencies with the @Inject annotation, so the callers of my AwesomenessFinder won’t be affected. For example, the following in a client class will still work:

@Inject
private AwesomenessFinder awesomenessFinder;

However, the big difference is that I autowire at the constructor level, which gives me a clean way to inject mock implementations. And, since we’re mocking, let’s use a mocking library. Last year, I wrote a post about mockito where I used ugly setters to inject my mocks. With the technique mentioned here, I don’t need to expose my dependencies anymore, I get a much better encapsulation. Here is what the updated test case looks like:

public class AwesomenessFinderTest {

  @Test
  public void checkInterestingBlog_bookmarked() {
    BookmarkService bookmarkService = mock(BookmarkService.class);
    when(bookmarkService.contains(anyString())).thenReturn(false);

    BlogAnalyzer blogAnalyzer = mock(BlogAnalyzer.class);
    when(blogAnalyzer.isInteresting(anyString())).thenReturn(true);

    AwesomenessFinder awesomenessFinder = new AwesomenessFinder(blogAnalyzer, bookmarkService);

    String url = "http://www.javaspecialists.eu";
    awesomenessFinder.checkBlog(url);

    verify(bookmarkService).bookmark(url);
  }
}

Note that this is now plain java, there is no need to use Spring to inject the mocks. Also, the definition of those mock is in the same place than their usage which eases the maintenance. To go a step further, let’s implement other test cases. To avoid code duplication we’ll refactor the test class and introduce some enums to keep the test cases as expressive as possible.

public class AwesomenessFinderTest {

  private enum Knowledge {KNOWN, UNKNOWN};

  private enum Quality {INTERESTING, BORING};

  private enum ExpectedBookmark {STORED, IGNORED}

  private enum ExpectedAnalysis {ANALYZED, SKIPPED}

  @Test
  public void checkInterestingBlog_bookmarked() {
    checkCase(Knowledge.UNKNOWN, Quality.INTERESTING,
        ExpectedBookmark.STORED, ExpectedAnalysis.ANALYZED);
  }

  @Test
  public void checkBoringBlog_ignored() {
    checkCase(Knowledge.UNKNOWN, Quality.BORING,
        ExpectedBookmark.IGNORED, ExpectedAnalysis.ANALYZED);
  }

  @Test
  public void checkKnownBlog_ignored() {
    checkCase(Knowledge.KNOWN, Quality.INTERESTING,
        ExpectedBookmark.IGNORED, ExpectedAnalysis.SKIPPED);
  }

  private void checkCase(Knowledge knowledge, Quality quality,
                         ExpectedBookmark expectedBookmark, ExpectedAnalysis expectedAnalysis) {

    BookmarkService bookmarkService = mock(BookmarkService.class);
    boolean alreadyBookmarked = (knowledge == Knowledge.KNOWN) ? true : false;
    when(bookmarkService.contains(anyString())).thenReturn(alreadyBookmarked);

    BlogAnalyzer blogAnalyzer = mock(BlogAnalyzer.class);
    boolean interesting = (quality ==  Quality.INTERESTING) ? true : false;
    when(blogAnalyzer.isInteresting(anyString())).thenReturn(interesting);

    AwesomenessFinder awesomenessFinder = new AwesomenessFinder(blogAnalyzer, bookmarkService);

    String url = "whatever";
    awesomenessFinder.checkBlog(url);

    if (expectedBookmark == ExpectedBookmark.STORED) {
      verify(bookmarkService).bookmark(url);
    } else {
      verify(bookmarkService, never()).bookmark(url);
    }

    if (expectedAnalysis == ExpectedAnalysis.ANALYZED) {
      verify(blogAnalyzer).isInteresting(url);
    } else {
      verify(blogAnalyzer, never()).isInteresting(url);
    }
  }
}

Last but not least, a nice bonus to the injection by constructor is the capacity to have all the dependencies of a class in the same place (the constructor). If the list of dependencies grow beyond control, you get a very obvious code smell with the size of the constructor. It’s a sign that you certainly have more than one responsibility in your class and you should split it into multiple classes, easier to isolate for unit testing.
 

Reference: To autowire or not to autowire from our JCG partner Damien Lepage at the Programming and more blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

4 Responses to "Spring: To autowire or not to autowire"

  1. Aldric says:

    Hi!
    I prefer your first implementation :)
    I’m not a fan of constructor injection.

    Why don’t you simply use mockito annotations in order to inject mocks?
    See @Mock and @injectMock.

  2. Aldric says:

    Haa! Yes I prefer this link :o)

    What I don’t like with constructor injection is that we display too many information (and sometime purely internal information) to the outside.

    It’s more explicit for the one creating/maintaining tests
    but could be misleading for a developer who try to use our classes (specially when we found out constructors with 10 parameters ;o) )

    Note that I think the same for setter injection.

    • Damien Lepage says:

      The fact that constructor injection becomes annoying with 10 parameters can be seen as a good thing. It’s a smell that your class has too many dependencies, so most likely more than one responsibility, and should be refactored. I’ve been taught this by some TDD guru and I must say it proved to be a useful indicator in my recent experience.

      I agree that it’s exposing some implementation details to the outside. But unless it’s a public API, I think it’s worth the trade-off.

  3. Damien Lepage says:

    Interesting library, @ReplaceWithMock seems to be doing the same thing than @InjectMocks. I guess the Mockito annotation was created after.

Leave a Reply


+ 4 = thirteen



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close