Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I JUnit test a Spring autowired constructor?

I have done quite a bit of searching online and I can't find an example of unit testing with an autowired constructor. I am using Spring to autowire in the values from a properties file to my application. I want to unit test MyApp.java's start method, but I have an autowired constructor so I don't know how to instantiate MyApp. Without the autowired properties, I was doing this in my unit test:

@Test
public void testStart() {
  try{
   MyApp myApp = new MyApp();
   myApp.start();
   }
   catch (Exception e){
     fail("Error thrown")
   }
}

I don't want to mock the autowiring, as I need to obtain the values from the properties file and to further complicate things, I am configuring everything through annotations. I don't have a spring.xml, application-context.xml, or a web.xml file. So how do I go about instantiating/testing MyApp's start method? I tried adding in @RunWith(SpringJUnit4ClassRunner.class) and autowiring MyApp myApp, but it throws errors about failing to load the application context that aren't fixed by implementing ApplicationContextAware on the test class.

Here is MyApp.java

@Component
public class MyApp {

    private static ApplicationContext applicationContext;
    private static MyAppProperties myAppProperties;

    //Obtain the values from the app.properties file
    @Autowired
    MyApp(MyAppProperties myAppProps){
        myAppProperties = myAppProps;
    }

    public static void main(String[] args) throws Exception {
     // Instantiate the application context for use by the other classes
     applicationContext = new AnnotationConfigApplicationContext("com.my.company");

     start();
    }

    /**
     * Start the Jetty server and configure the servlets
     * 
     * @throws Exception
     */
    public static void start() throws Exception {
        // Create Embedded Jetty server
        jettyServer = new Server();

        // Configure Jetty so that it stops at JVM shutdown phase
        jettyServer.setStopAtShutdown(true);
        jettyServer.setStopTimeout(7_000);

        // Create a list to hold all of the handlers
        final HandlerList handlerList = new HandlerList();

        // Configure for Http
        HttpConfiguration http_config = new HttpConfiguration();
        http_config.setSecureScheme("https");
        http_config.setSecurePort(myAppProperties.getHTTP_SECURE_PORT());
    ....
    }
}

Here is my app.properties file

# Spring Configuration for My application

#properties for the embedded jetty server
http_server_port=12345

Here is MyAppProperties.java

@Component
public class MyAppProperties implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    //List of values from the properties files to be autowired
    private int HTTP_SERVER_PORT;
    ...

    @Autowired
    public MyAppProperties( @Value("${http_server_port}") int http_server_port, ...){
        this.HTTP_SERVER_PORT = http_server_port;
    }

    /**
     * @return the applicationContext
     */
    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * @param applicationContext
     *            the applicationContext to set
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * @param name
     *            the name to set
     */
    public void setHTTP_SERVER_PORT(String name) {
        JETTY_SERVER_NAME = name;
    }

    /**
     * @return the httpServerPort
     */
    public int getHTTP_SERVER_PORT() {
        return HTTP_SERVER_PORT;
    }
}

Here is MyAppTest.java

@RunWith(SpringJUnit4ClassRunner.class)
public class MyAppTest implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        applicationContext = appContext;    
    }

    @Autowired
    private MyApp myapp;

    @Test
    public void testStart(){
    try {
        if(myapp != null){
            myapp.start();
        }
        else{
            fail("myapp is null");
        }
    } catch (Exception e) {
        fail("Error thrown");
        e.printStackTrace();
    } 
    }
}

UPDATE: Here is my configuration class

@Configuration
@Component
public class ApplicationConfig implements ApplicationContextAware {

    private final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfig.class);
    private ApplicationContext applicationContext;

    /**
     * @return the applicationContext
     */
    public ApplicationContext getApplicationContext() {
        LOGGER.debug("Getting Application Context", applicationContext);
        return applicationContext;
    }

    /**
     * @param applicationContext
     *            the applicationContext to set
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    // Needed for @Value
    /**
     * Property sources placeholder configurer.
     *
     * @return the property sources placeholder configurer
     */
    @Bean
    public PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer() {
        PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
        propertyPlaceholderConfigurer.setLocation(new ClassPathResource("app.properties"));
        return propertyPlaceholderConfigurer;
    }
    ...
}
like image 485
jencoston Avatar asked Jun 09 '16 13:06

jencoston


1 Answers

We can mock the objects using the jmockito framework.

Using @InjectMocks for dependency injection via Mockito You also have the @InjectMocks annotation which tries to do constructor, method or field dependency injection based on the type. The following code is a slightly modified example from the Javadoc.

// Mockito can construct this class via constructor
public class ArticleManager {
         ArticleManager(ArticleCalculator calculator, ArticleDatabase database) {
        }
}

// Mockito can also perform  method injection
public class ArticleManager {
        ArticleManager() {  }
        void setDatabase(ArticleDatabase database) { }
        void setCalculator(ArticleCalculator calculator) { }
}

// Mockito can also perform  field injection
public class ArticleManager {

    private ArticleDatabase database;
    private ArticleCalculator calculator;
}

The following will be the unit test class.

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest  {
   @Mock private ArticleCalculator calculator;
   @Mock private ArticleDatabase database;
   @Spy private UserProvider userProvider = new ConsumerUserProvider();

   // creates instance of ArticleManager
   // and performs constructor injection on it
   @InjectMocks private ArticleManager manager;

   @Test public void shouldDoSomething() {
           // assume that ArticleManager has a method called initialize which calls a method
           // addListener with an instance of ArticleListener
           manager.initialize();

       // validate that addListener was called
           verify(database).addListener(any(ArticleListener.class));
   }

}

Make sure that you are using @RunWith(MockitoJUnitRunner.class) For more information see http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html.

like image 132
Sudhakar Avatar answered Oct 18 '22 19:10

Sudhakar