2

I have a servlet that makes use AnnotationConfigApplicationContext , I want to test this class. How do i mock AnnotationConfigApplicationContext or is there way to test below class. I dont want to use spring-auto-mock due to very specifics reasons.

Below is the code

 @WebServlet("/Application")
public class Application extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    
    private static final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String birthPlace = request.getParameter("birthPlace");
        PersonData person = new PersonData();
        person.setBirthPlace(birthPlace);
        person.setName(name);
        PersonDao dao = context.getBean(PersonDao.class);
        dao.insertPerson(person);
    }

}

@Configuration
@ComponentScan(basePackages = { "com.test.*" })
public class JavaConfig {

    @Bean
    public NamedParameterJdbcOperations getLettuceConnectionFactory() {
        return new NamedParameterJdbcTemplate(mysqlDataSource());
    }

    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");
        return dataSource;
    }
}

Below is the code that i have written so far

public class ApplicationTest {
@Mock
AnnotationConfigApplicationContext context;

@Test
public void testApplication() throws Exception{
    Application app = new Application();
    PowerMockito.whenNew(AnnotationConfigApplicationContext.class).withAnyArguments().thenReturn(context);
    app.process();
}

}

I see that all beans in JavaConfig are trying get generated , since I will not be able to access database from Junit the bean creation failes, but I dont want bean to be created in first place.

Am I initiating mock for AnnotationConfigApplicationContext correctly? is the issue due to AnnotationConfigApplicationContext private static field? How do i handle this scenario.

Can junit testing for above class be achieved without Power Mockito if i remove static final field?

sumedha
  • 473
  • 1
  • 9
  • 24
  • Why? Are there more classes like the `Application` class? As I would considered it flawed in the first place (generally when something is hard to test, there is something wrong with the design is good rule of thumb). – M. Deinum Jul 01 '20 at 18:36
  • There is only one Application class – sumedha Jul 02 '20 at 15:05
  • Nonetheless it is flawed and this isn't the way you should be using Spring. – M. Deinum Jul 02 '20 at 17:16
  • What could be the possible solution for such a class. Since its deployed as standalone code , i need to use main method. I want to initiate spring only once . I can make AnnotationConfigApplicationContext instance variable of some class but i need to initiate thtat class through main method which would mean beans will be loaded each time main method is called. – sumedha Jul 02 '20 at 17:20
  • And why would the main method be called more than once? That is a bootstrap method to run the program you generally run it once and then keep it running or restart. – M. Deinum Jul 02 '20 at 17:23
  • There are different clients to this code which would be invoking this multiple times, i cannot change this code to Rest service – sumedha Jul 02 '20 at 17:33
  • And those client are using Spring? – M. Deinum Jul 02 '20 at 17:51
  • Actually its http request , Application.java is actually a servlet , i just represented as standalone java for simplicity – sumedha Jul 03 '20 at 06:42
  • Please post the actual code, instead of some dumbed down version. If it is a servlet are you deploying it or is it part of others systems libraries? Who is running it. – M. Deinum Jul 03 '20 at 07:54
  • I have updated the code in question section – sumedha Jul 03 '20 at 16:00

1 Answers1

0

You will not be able to mock AnnotationConfigApplicationContext with the current implementation. You will need to use dependency injection. Create a @Bean in your Java Config class like this.

@Bean
public AnnotationConfigApplicationContext getAnnotationConfigApplicationContext() {
  return new AnnotationConfigApplicationContext(JavaConfig.class);
}

Then in your Application class:

@WebServlet("/Application")
public class Application extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    **private final AnnotationConfigApplicationContext context;**

    **Application(AnnotationConfigApplicationContext annotationConfigApplicationContext) {
      this.annotationConfigApplicationContext = annotationConfigApplicationContext;
    }**

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // code here
    }

}

M. Dienum is right. This is not how Spring should be used.

The right thing to do would be to inject The PersonDao into the Application class. Then you would mock the PersonDao class in your ApplicationUnitTest.

To do that it would look like this:

@WebServlet("/Application")
public class Application extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    **private final PersonDao dao;**

    **Application(PersonDao dao) {
      this.dao= dao;
    }**

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       String name = request.getParameter("name");
        String birthPlace = request.getParameter("birthPlace");
        PersonData person = new PersonData();
        person.setBirthPlace(birthPlace);
        person.setName(name);
       
        dao.insertPerson(person);
    }
}

And your test would look something like this:

public class ApplicationTest {

PersonDao personDao = mock(PersonDao.class);
Application app = new Application(personDao);

@Test
public void testApplication() throws Exception{
    doNothing().when(personDao).insert(any());
    
    app.doPost(request, response);
    verify(personDao).insert(person);
    // fill in the details
    // not sure what app.process is supposed to be
}

The Spring framework has MockMvc for reasons like this. It makes interacting with endpoints easier in tests.

Steven Diamante
  • 207
  • 2
  • 7
  • i dont think the constructor Application(AnnotationConfigApplicationContext annotationConfigApplicationContext) , will be invoked, please let me know if i am wrong – sumedha Jul 14 '20 at 15:43