简体   繁体   中英

How to do integrating test in a spring MVC controller which has factory method?

I have a spring controller which has outter environment dependency in the requesting mapping method and the dependency was intruduced by a factory single-instance method:

@RequestMapping(path = "/video_course", method = RequestMethod.POST)
  public JSONObject createVideoCourse(@RequestBody @Valid VideoCourseDTO dto,
      HttpServletRequest request) {
    try {
      ContextManagerFactory.getInstance().setContext(request); // How to mock the ContextManagerFactory in unit testing?
      Context ctx = ContextManagerFactory.getInstance().getContext();
      Course course = ctx.getCourse();
      Content parentContent = ctx.getContent();

      long id = videoCourseService.addVideoCourse(dto.title, dto.available,
          String.join(",", dto.keywords), dto.videos, course, parentContent, dto.tracking);
      JSONObject json = new JSONObject();
      json.put("vc_id", id);
      return json;

    } catch (Exception e) {
      logger.error("createVideoCourse", e);
      throw new ServerInternalException(e.getMessage());
    }
  }

With the following junit test case:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {SpringMvcTestConfig.class, HibernateDevConfig.class,
    BlackboardEnvironmentConfig.class, VideoRepoTestConfig.class, VideoCourseTestConfig.class})
@Sql("classpath:video_course.sql")
@Transactional
@ActiveProfiles("dev")
public class VideoCourseApiTest {

  private static Logger logger = LoggerFactory.getLogger(VideoCourseApiTest.class);

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
  }

  @Test
  public void testCreateVideoCourse() throws Exception {
    JSONObject json = new JSONObject();
    // construct the request body json string ...
    logger.debug("request body: \n{}",json.toJSONString());
    mockMvc
        .perform(post("/course/video_course").content(json.toJSONString())
            .contentType(MediaType.APPLICATION_JSON))
        .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("vc_id", is(greaterThan(0))));
  }

}

And I don't know how to mock the ContextManagerFactory with Mockito in such situation. Anyone could help me? Thanks.

Sorry, but I think that this approach is a bit 'harmful'. I mean, using static classes in Controllers. And, on the other side, it creates a class that, as you've found, it's impossible to test.

To create a better separation and isolation between classes ( SOLID can, and should, be used also in controllers... ), I would create a service (or two, each one with it's bounded functionality) that has the only purpose to extract Course and Content from the ContextManagerFactory .

Then, autowiring this in the controller (at the constructor) and testing the whole stuff becomes simple as a breeze. This because, at this point, you don't need to access the database to test the controller: what do you need are just mocks of the services that give a precise answer for a given input. At the end this is the reason of Mockito.

Finally, testing the query will become an integration test of videoCourseService , but this will not require the controller nor the ContextManagerFactory . It just needs the minimum configuration files that are used by the service to execute the query.

About the controller, you can use the MockMvcBuilders.standaloneSetup function, that gives you control about how the controller is built and doesn't require to load any configuration file. You just have to identify what you need and mock or build it to create the controller.

For example, I use this to check that the urls are correct and the controller returns correct json data.

@Category(AnInterface.class)
public class MyControllerTest {

   @Mock
   View mockView;

   @Mock
   [OurWebBindingInitializer] webBindingInitializer = new OurWebBindingInitializer();

   MockMvc mockMvc;

   MyStub myStub = new MyStub();


   @Before
   public void setUp()
        throws Exception {
      MockitoAnnotations.initMocks(this);
      mockMvc = standaloneSetup(new MyController(myStub))
          .setConversionService(TestConversionServiceBuilder.create())
            .setSingleView(mockView)
            .build();
   }

   ...

} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM