Search code examples
javaspringunit-testingspring-annotations

Spring annotations - good or bad?


I recently cam across a class like this:

public class SomeDao {
   @Inject
   private DataSource dataSource;

   @Value("#{someMap['someDao.sql']}")
   private String sql;

   private JdbcTemplate jdbcTemplate;

   @PostConstruct
   private void postConstruct() {
      jdbcTemplate = new JdbcTemplate(dataSource);
   }

   ...
}

Now I would like to unit test this class by injecting the DataSource and the SQL string. As far as I can see, there are two options for this:

  1. Load up an application context which will be slow and most likely load a whole lot of stuff that I don't need for the test
  2. Use reflection to set the private properties and call the private postConstruct() method

In the days before spring annotations, the class would have been written like this:

public class SomeDao {
   private String sql;
   private JdbcTemplate jdbcTemplate;

   public SomeDao(DataSource dataSource, String sql) {
      this.jdbcTemplate = new JdbcTemplate(dataSource);
      this.sql = sql;
   }    
   ...
}

And I could have easily tested this class without the need for reflection or spring. My class was a pure pojo and had no spring dependencies.

So, are spring annotations a good thing or are they a step backwards? Are there times where I shoulud be using them and times when I should use the old XML app context?

Thanks, Lance.


Solution

  • Why not declare a test-context with mocks of beans and inject those needed by your class from that? That's what people usually do and it's quite straightforward.

    The most lightweight way of doing this is to provide an inner class inside your test class, annotated with @Configuration that has methods that provide mocks:

    @Configuration
    public class DataAccessConfiguration {
    
        @Bean
        public DataSource dataSource() {
            DataSource dataSource =  mock(Datasource.class);
            return dataSource;
        }
    
        @Bean
        public Map<String,String> someMap() {
            Map<String, String> map = new HashMap<>();
            map.put("someDao.sql", "somevalue");
            return map;
        }
    
    }
    

    So instead of giving up on autowiring, you can actually take advantage of it. Also you limit the loaded context to just what is needed by the class under test.