【问题标题】:Mocked service function not working as expected模拟服务功能未按预期工作
【发布时间】:2021-03-08 23:16:40
【问题描述】:

这是我的代码。我不明白为什么它不起作用。问题在于 test 中的行:

when(applicationUserRepository.findApplicationUserByUsername("testUser"))
        .thenReturn(userToReturnFromRepository);

似乎什么也没做。当函数 findApplicationUserByUsername 应该返回 userToReturnFromRepository 的可选项时,它会返回一个空的可选项。

控制器:

@RestController
@RequestMapping("api/v1/exercises")
public class ExerciseController {
    
    @Autowired
    ExerciseService exerciseService;

    @GetMapping
    public List<Exercise> getExercises() {

        List<Exercise> exercises = exerciseService.getAllExercises();
        return exercises;
    }
}

服务:

@Service("exerciseService")
public class ExerciseService {
    
    @Autowired
    ExerciseRepository exerciseRepository;

    @Autowired
    ApplicationUserRepository applicationUserRepository;
 
    @Transactional
    public List<Exercise> getAllExercises() {
        
        Principal principal = SecurityContextHolder.getContext().getAuthentication();

        Optional<ApplicationUser> applicationUser = applicationUserRepository.findApplicationUserByUsername(principal.getName());
                
        List<Exercise> exercises = new ArrayList<>();
        if(applicationUser.isPresent()){
            exercises = applicationUser.get().getExercises();
               
            }

        return exercises;
    }
}

测试:

@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {

    private final MockMvc mockMvc;
    private final ObjectMapper objectMapper;
    @Mock
    ApplicationUserRepository applicationUserRepository;
    
    @Autowired
    public ExerciseControllerTest(MockMvc mockMvc,
            ApplicationUserRepository applicationUserRepository, ObjectMapper objectMapper) {
        this.mockMvc = mockMvc;
        this.applicationUserRepository = applicationUserRepository;
        this.objectMapper = objectMapper;
    }

    @BeforeEach
    public void initMocks() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    @WithMockUser(username = "testUser")
    public void testGetExercises() throws Exception {
        
         Exercise ex = new Exercise();
         ex.setData("test");
         ApplicationUser user = new ApplicationUser();
         Exercise[] exercises = {ex};
         List<Exercise> list = new ArrayList<Exercise>(Arrays.asList(exercises));
         user.setExercises(list);
         Optional<ApplicationUser> userToReturnFromRepository = Optional.of(user);

        when(applicationUserRepository.findApplicationUserByUsername("testUser"))
        .thenReturn(userToReturnFromRepository);

        mockMvc.perform(get("/api/v1/exercises")).andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));


    }
}

【问题讨论】:

    标签: java spring mockito junit5


    【解决方案1】:

    您的测试中发生了两件相互矛盾的事情:

    1. 您正在使用 Mockito 通过反射来初始化模拟实现
    2. 您已经通过构造函数将 ApplicationUserRepository 注入到测试类中。

    最终发生的事情是这样的:

    1. spring 将 applicationUserRepository 注入到构造函数参数中
    2. applicationUserRepository 字段在构造函数中设置为 spring 注入版本
    3. Mockito 初始化一个新的 applicationUserRepository 模拟
    4. Mockito 将 applicationUserRespository 字段替换为 mock(即,再见了您的 MVC 设置正在使用的 spring bean 的句柄!)

    我能想到的最简单的修复方法是使用@MockBean 而不是@Mock 结合构造函数注入。 @MockBean 将指示 Spring 为您创建模拟实例,使用它,并在测试中提供给您。

    @SpringBootTest
    @AutoConfigureMockMvc(addFilters = false)
    public class ExerciseControllerTest {
    
        private final MockMvc mockMvc;
        private final ObjectMapper objectMapper;
        @MockBean // User MockBean instead of Mock
        ApplicationUserRepository applicationUserRepository;
        
        @Autowired
        public ExerciseControllerTest(MockMvc mockMvc, ObjectMapper objectMapper) {
            this.mockMvc = mockMvc;
            //remove the applicationUserRepository injection
            this.objectMapper = objectMapper;
        }
    
    // remove MockitoAnnotations.openMocks(this);
    //...
    
    ...
    }
    
    

    【讨论】:

    • 它现在按预期工作。你能解释一下为什么我需要 MockBean 而不是 Mock 吗?我不明白它有什么不同...
    • 是的。 “@Mock”是一个纯 Mockito 注释。当您调用 MockitoAnnotations.openMocks(this) 时,类实例将填充模拟字段,但 Spring 应用程序上下文对此一无所知。 “@MockBean”是一个 Spring 注解。 Spring 将使用 Mockito 初始化模拟并将其添加到 Spring 应用程序上下文中。
    猜你喜欢
    • 2013-08-29
    • 2020-04-28
    • 2021-08-27
    • 1970-01-01
    • 2012-10-07
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多