【问题标题】:Unable to invoke method in mocked service from mocked controller using mockito with spring web mvc无法使用带有 spring web mvc 的 mockito 从模拟控制器调用模拟服务中的方法
【发布时间】:2015-02-14 11:01:04
【问题描述】:

我正在尝试为 spring mvc rest 控制器和控制器访问的服务创建一个 JUnit 测试用例。
我正在使用 Mockito 来执行上述操作。我能够从测试用例成功调用模拟注入控制器,并且当我调试时,我看到模拟的 userService 在模拟的控制器中可用,但服务对象中的方法没有被调用。在调试时,我观察到它只是跳过了服务方法调用。
我也没有看到任何异常。

我正在使用

  1. Maven 3
  2. Spring 4.1.1 发布版
  3. Junit 4.11
  4. Java 1.6 版

我在下面粘贴了我的代码:

1.测试类

    package controller;

    import java.io.IOException;

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.RequestBuilder;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.myPackage.model.User;
    import com.myPackage.service.IUserService;

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:/spring/application-config.xml")
    public class UserControllerTest {

        private MockMvc mockMvc;

        @Mock
        private IUserService userService;

        @InjectMocks
        private UserController  controller;

        @Before
        public void setup(){
            MockitoAnnotations.initMocks(this);
            this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        }

        @Test
        public void testRegisterUser()throws Exception{
            User user = new User();
            user.setCity("City");
            user.setTown("Town");
            user.setFirstname("Name");
            user.setLastname("LastName");
            user.setPassword("abc@123");
            user.setUsername("abc@gmail.com");

            RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
                    .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));

            mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
        }

        public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsBytes(object);
        }
    }

2。被测控制器

    package com.myPackage.controller;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;

    import com.myPackage.model.User;
    import com.myPackage.service.IUserService;

    @RestController
    @RequestMapping("/service/user")
    public class UserController {
        private static final Logger logger = LoggerFactory.getLogger(UserController.class);

        @Autowired
        private IUserService userService;

        public IUserService getUserService() {
            return userService;
        }

        public void setUserService(IUserService userService) {
            this.userService = userService;
        }

        @RequestMapping(value = "/register/", method = RequestMethod.POST,headers="Accept=application/json")
        public String registerUser(@RequestBody User user) {
            logger.info("register user:"+user.toString());
            String userDetails = userService.registerUser(user);
            logger.info("user registered:"+userDetails);
            return userDetails;
         }
    }

3.测试中要在控制器内调用的服务

服务方法 registerUser 将从上面的控制器中调用。当我们运行测试用例时,我没有看到任何日志打印在控制台上。此外,在调试时,我看到这样的 userSerivice 实例 - IUserService$$EnhancerByMockitoWithCGLIB$$c00081eb 已创建,但是当我深入挖掘所有内容以查看模拟处理程序注册方法下的方法列表时,我只在列表中看到“toString”方法调用。不确定这是否说明了为什么下面的服务类中的这个方法“registerUser”在测试用例期间没有被调用。

package com.myPackage.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

import com.myPackage.dao.IUserDao;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;

@Service("userService")
public class UserService implements IUserService {

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

    @Autowired
    private IUserDao userDao;

    public IUserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String registerUser(User user) {
        // TODO Auto-generated method stub
        logger.info("New User for registration:",user);
        if(user!=null && user.getUsername()!=null){
            User alreadyExist = userDao.getUserByLogin(user.getUsername());
            if(alreadyExist!=null){
                return "userAlreadyExists";
            }
        }
        logger.info("New User for registration complete:",user.getUser_id());
        return null;
    }
}

4.上面第 3 点中 UserService 类实现的接口

IUserService。上面测试类中模拟的 userService 是 IUserService 类型。

package com.myPackage.service;

import com.myPackage.model.User;
import com.myPackage.model.UserInfo;

public interface IUserService {

    public String registerUser(User user);

}

【问题讨论】:

    标签: spring-mvc mockito junit4


    【解决方案1】:

    您在 userService 中的方法(读取代码)将永远不会从您的控制器中实际调用,因为它已被 mockito 模拟出来。

    你自己定义了这个:

    @Mock
    private IUserService userService;
    
    @InjectMocks
    private UserController  controller;
    
    MockitoAnnotations.initMocks(this);
    

    如果您想断言该方法已被调用,您可以使用,

    verify(userService, times(1)).registerUser(any(User.class));
    

    【讨论】:

    • :感谢您的洞察。如果我不模拟 userService,那么在单元测试时,在匹配 registerUser 的 userController 方法下发现 userService 为 null。因此,在某处阅读后,我嘲笑了 userService。同样在按照您的建议进行更改后,仍然没有调用模拟的服务方法-以下是我的更改-mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk()); Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
    • userService 为空,因为它可能在 spring 上下文中不可用。在您的测试中为“com.myPackage.service”添加@Component 扫描。对于另一个问题,它在使用模拟对象时永远不会被实际调用。
    • 再次感谢您的宝贵意见。使用 @ComponentScan("com.myPackage.service") 也没有帮助,服务实例在被测控制器中仍然为空。但我理解的是,如果我尝试模拟,那么我将能够调用该服务,因为它是只是下面 Khalid 提到的一个存根。令人失望的是,我接受我不能在保持控制器处于测试状态的同时调用服务中的方法。我希望有一条出路,因为我想在一个测试用例中检查完整的流程。
    【解决方案2】:

    @Jonas:感谢您的洞察力。如果我不模拟 userService,那么在单元测试时,在匹配 registerUser 的 userController 方法下发现 userService 为 null。因此,在某处阅读后,我嘲笑了 userService。同样在按照您的建议进行更改后,仍然没有调用模拟的服务方法 - 以下是我的更改 -

    RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
                        .contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
    
    mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
    Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
    

    【讨论】:

      【解决方案3】:

      如果你想测试实际的服务方法,那么自动装配它:

      @Autowired
      private IUserService userService;
      

      如果你想像现在这样使用模拟,你需要存根方法:

      when(userService.registerUser(any(User.class))).thenReturn("expected string");
      

      【讨论】:

      • 感谢 Khalid 的投入。 @Autowired 没有让服务类实例化,但存根方法让我明白我不能调用该方法,我所能做的就是复制行为。虽然我希望有一种方法可以在单个测试用例中测试从控制器到服务再到 dao 的完整流程。
      • 你可以用integration testing做到这一点。
      • 如果您检查我上面的测试类,我已经在关注 Spring Integration 测试文档中提到的内容。我们在测试开始时加载应用程序上下文,在测试类顶部使用以下代码并使用 SpringJUnit4ClassRunner - @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:/spring/application-config.xml") @WebAppConfiguration 公共类 UserControllerTest {}
      猜你喜欢
      • 2016-08-18
      • 1970-01-01
      • 2016-12-11
      • 2012-11-19
      • 2013-04-16
      • 1970-01-01
      • 2015-11-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多