Java中如何模拟类属性及方法?服务层方法单元测试求教
针对
joinTournamentUser方法的单元测试方案 首先你之前尝试的@Mock boolean TournamentServiceImpl.tournamentTimeOfTheDay写法是错误的——Mockito的@Mock用来模拟对象实例,不能直接模拟基本类型属性。下面分场景给出具体实现方案:
1. 控制布尔属性tournamentTimeOfTheDay的值
因为这是类的成员属性,不需要Mock,直接通过反射设置值即可:
@InjectMocks private TournamentServiceImpl tournamentService; @Test(expected = NotEnoughCredits.class) public void testJoinTournament_NotInTournamentTime() { // 反射设置属性为false,触发第一个异常分支 ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", false); tournamentService.joinTournamentUser(1L); } @Test public void testJoinTournament_InTournamentTime() { // 设置属性为true,让流程进入后续判断 ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", true); // 后续编写正常流程的测试逻辑 }
如果不用Spring的ReflectionTestUtils,也可以自己写简单的反射代码实现属性赋值。
2. 模拟List属性alreadyInGame的contains()行为
分两种情况处理:
- 如果
alreadyInGame是注入的依赖:直接Mock这个List实例并注入服务@Mock private List<Long> alreadyInGame; @InjectMocks private TournamentServiceImpl tournamentService; @Before public void setup() { MockitoAnnotations.openMocks(this); } @Test(expected = NotEnoughCredits.class) public void testJoinTournament_AlreadyInGame() { // 指定调用contains(1L)时返回true,触发第二个异常分支 when(alreadyInGame.contains(1L)).thenReturn(true); // 先确保比赛时间属性为true ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", true); tournamentService.joinTournamentUser(1L); } - 如果
alreadyInGame是服务内部创建的:用反射把内部List替换为Mock实例@InjectMocks private TournamentServiceImpl tournamentService; @Mock private List<Long> mockAlreadyInGame; @Before public void setup() { MockitoAnnotations.openMocks(this); // 替换服务内部的alreadyInGame为Mock实例 ReflectionTestUtils.setField(tournamentService, "alreadyInGame", mockAlreadyInGame); } @Test(expected = NotEnoughCredits.class) public void testJoinTournament_AlreadyInGame() { when(mockAlreadyInGame.contains(1L)).thenReturn(true); ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", true); tournamentService.joinTournamentUser(1L); }
3. 测试map相关的匹配逻辑
通过反射给map设置不同内容,覆盖两种返回场景:
- 场景1:5个国家全存在,返回"Match starting..."
@Test public void testJoinTournament_MatchStarts() { ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", true); when(mockAlreadyInGame.contains(1L)).thenReturn(false); // 填充包含所有5个国家的map Map<String, String> fullMap = new HashMap<>(); for (UserServiceImpl.Country country : UserServiceImpl.Country.class.getEnumConstants()) { fullMap.put("test-key-" + country.countryName, country.countryName); } ReflectionTestUtils.setField(tournamentService, "map", fullMap); String result = tournamentService.joinTournamentUser(1L); assertEquals("Match starting...", result); } - 场景2:缺少部分国家,返回"Waiting for match to start..."
@Test public void testJoinTournament_WaitingForMatch() { ReflectionTestUtils.setField(tournamentService, "tournamentTimeOfTheDay", true); when(mockAlreadyInGame.contains(1L)).thenReturn(false); // 只填充4个国家的map Map<String, String> partialMap = new HashMap<>(); UserServiceImpl.Country[] countries = UserServiceImpl.Country.class.getEnumConstants(); for (int i = 0; i < countries.length - 1; i++) { partialMap.put("test-key-" + countries[i].countryName, countries[i].countryName); } ReflectionTestUtils.setField(tournamentService, "map", partialMap); String result = tournamentService.joinTournamentUser(1L); assertEquals("Waiting for match to start...", result); }
额外注意
- 方法的
synchronized修饰符不影响单元测试,Mockito和JUnit会正常处理调用 - 异常类型
NotEnoughCredits命名语义不准确(比如"不在比赛时间"应该对应专属异常),建议后续优化,但不影响当前测试实现 - 所有私有属性都需要通过反射工具操作,Mockito无法直接Mock私有成员
内容的提问来源于stack exchange,提问作者C.Unbay
相关产品推荐
相关产品推荐

