1
votes

I have a Spring MVC application. It has Controller, Service and Dao. I would like to test only the Controller and Service by Mocking the DAO layer using Mockito.

My Controller class:

@Controller
@RequestMapping(value="/audit")
public class AuditController {

   @Autowired
   AuditService auditService;

   ...
}

My Service class:

@Service
public class AuditService {

   @Autowired
   AuditDao auditDao;

   ....
}

My Test class:

@RunWith(SptringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/dispatcher-servlet.xml", "spring-context.xml"})
@WebAppConfiguration
public class AuditControllerTest {

   private MockMvc mockMvc;

   @Mock
   AuditDao auditDao;

   @Autowired
   private WebApplicationContext webApplicationContext;

   @Before
   public void setUp() {
       MockitAnnotations.initMocks(this);
       mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
   }


  @Test
  public void testGetAudit() {

      Mockito.when(auditDao.getAudit(Mockito.any(Long.class))).thenReturn(new Audit(1L));
      mockMvc.perform(get("/audit/{id}", "1")).andExpect(status().isOk());
  }
}

PROBLEM: It performs the call fine by going through autowired controller and Service. However, from the Service the DAO calls are going to a real DAO not the Mocked DAO.

  1. I understand that the DAO is autowired to the real Dao, however I am not sure how to replace that Dao with the Mock one from the Test.

  2. Keeping the Dao in the controller and using @InjectMock to the controller works fine, but I want to keep the Dao in the Service and test only the controller and Service, but mock the Dao alone.

  3. I suspect that this issue is to do with the contexts (web application context and the MockMvc context), however I am not sure how to resolve it.

Any help would be greatly appreciated. Thanks in advance.

1
There may be a misunderstanding of some of the terms used, as in my opinion there are some questionable design choices here. I would suggest reviewing the current design to follow a more SOLID approach.Nkosi
@Nkosi can you please give some specific examples?KayKay
Ok with the name change, it changes my original statement about the terms used. With regards to my design statement, Autowired fields hide the classes dependencies. You assessment of the actual dao being called is accurate so I am looking into that.Nkosi
Wait. Did you try @InjectMock on the Service?Nkosi
In the mean time some interesting reading tedvinke.wordpress.com/2014/02/13/…Nkosi

1 Answers

1
votes

First I would suggest avoiding Autowired fields and have you class explicitly expose their dependencies via constructor dependencies

Controller class:

@Controller
@RequestMapping(value="/audit")
public class AuditController {
    private final AuditService auditService;

    @Autowired
    public AuditController(AuditService auditService) {
        this.auditService = auditService
    }

    //...
}

Service class:

@Service
public class AuditService {            
    private final AuditDao auditDao;

    @Autowired
    public AuditService(AuditDao auditDao) {
        this.auditDao = auditDao;
    }

    //....
}

I was thinking of something along the line of

@RunWith(SptringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/dispatcher-servlet.xml", "spring-context.xml"})
@WebAppConfiguration
public class AuditControllerTest {

    private MockMvc mockMvc;

    @Mock
    AuditDao auditDao;

    @InjectMock
    AuditService auditService;

    @Before
    public void setUp() {
        MockitAnnotations.initMocks(this);
        AuditController controller = new AuditController (auditService);
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testGetAudit() {

      Mockito.when(auditDao.getAudit(Mockito.any(Long.class))).thenReturn(new Audit(1L));
      mockMvc.perform(get("/audit/{id}", "1")).andExpect(status().isOk());
    }
}

But am uncertain if it will behave as expected when exercised.