Java 模拟服务功能未按预期工作

Java 模拟服务功能未按预期工作,java,spring,mockito,junit5,Java,Spring,Mockito,Junit5,这是我的密码。我不明白为什么它不起作用。问题在于测试中的线路: when(applicationUserRepository.findApplicationUserByUsername("testUser")) .thenReturn(userToReturnFromRepository); 似乎什么都没做。当函数findApplicationUserByUsername应该返回userToReturnFromRepository的可选值时,它返回一个空的可

这是我的密码。我不明白为什么它不起作用。问题在于测试中的线路:

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;
    }
}
@RestController
@请求映射(“api/v1/练习”)
公共类执行控制器{
@自动连线
运动服务;
@GetMapping
公开名单{
列表练习=exerciseService.getAllExercises();
返回练习;
}
}
服务:

@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;
    }
}
@Service(“exerciseService”)
公共课锻炼服务{
@自动连线
练习库练习库;
@自动连线
ApplicationUserRepository ApplicationUserRepository;
@交易的
公共列表getAllExercises(){
Principal=SecurityContextHolder.getContext().getAuthentication();
可选applicationUser=applicationUserRepository.findApplicationUserByUsername(principal.getName());
列表练习=新建ArrayList();
if(applicationUser.isPresent()){
exercises=applicationUser.get().getExercises();
}
返回练习;
}
}
测试:

@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)));


    }
}
@SpringBootTest
@自动配置emockmvc(addFilters=false)
公共类练习控制器测试{
私有最终MockMvc MockMvc;
私有最终ObjectMapper ObjectMapper;
@嘲弄
ApplicationUserRepository ApplicationUserRepository;
@自动连线
公共演习控制员测试(MockMvc MockMvc,
ApplicationUserRepository ApplicationUserRepository,ObjectMapper(对象映射器){
this.mockMvc=mockMvc;
this.applicationUserRepository=applicationUserRepository;
this.objectMapper=objectMapper;
}
@之前
公共void initMocks(){
MockitoAnnotations.openMocks(这个);
}
@试验
@WithMockUser(username=“testUser”)
public void testGetExercises()引发异常{
练习ex=新练习();
ex.setData(“测试”);
ApplicationUser用户=新的ApplicationUser();
练习[]练习={ex};
List List=newarraylist(Arrays.asList(练习));
用户设置练习(列表);
可选userToReturnFromRepository=Optional.of(用户);
当(applicationUserRepository.findApplicationUserByUsername(“testUser”))
。然后返回(用户从存储库返回);
mockMvc.perform(get(“/api/v1/exercises”).andExpect(status().isOk()).andExpect(jsonPath(“$”,hasSize(1));
}
}

在您的测试中发生了两件相互冲突的事情:

  • 您正在使用Mockito通过反射初始化模拟实现
  • 您已经将ApplicationUserRepository通过构造函数注入到测试类中
  • 最终发生的是:

  • spring将applicationUserRepository注入构造函数参数
  • applicationUserRepository字段在构造函数中设置为spring注入版本
  • Mockito初始化一个新的applicationUserRepository mock
  • Mockito用mock替换applicationUserRespository字段(也就是说,再见MVC安装程序正在使用的Springbean上的句柄!)
  • 我能想到的最简单的修复方法是使用@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)时,类实例将填充mock字段,但Spring应用程序上下文对此一无所知。“@MockBean”是一个Spring注释。Spring将使用Mockito初始化mock,并将其添加到Spring应用程序上下文中。