JUnit参数化测试中如何让Mock对象返回测试参数?
问题描述
使用JUnit的@ParameterizedTest结合枚举源编写测试时,尝试用枚举构造对象并让Mock方法返回该对象,运行时抛出InvalidUseOfMatchersException异常。
异常信息
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced or misused argument matcher detected here: You cannot use argument matchers outside of verification or stubbing. Examples of correct usage of argument matchers: when(mock.get(anyInt())).thenReturn(null); doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject()); verify(mock).someMethod(contains("foo"))
初始代码
枚举类
public static enum CodeEnum { S1("S1"), S2("S2") }
测试类(枚举源版本)
@RunWith(MockitoJUnitRunner.class) public class FooTest { @Mocked MockedObject mockedObject; @InjectMocks Foo underTest; @ParameterizedTest @EnumSource(CodeEnum.class) public void test_ParametrizedTest(CodeEnum enumParam) { Output expectedReturn = Output.builder().code(enumParam).build(); // Given when(mockedObject.method(any())) .thenReturn(expectedReturn); // when val result = underTest.method(); // then assertEquals(enumParam, result.getCode()); } }
尝试的解决方法(MethodSource版本)
改用@MethodSource提供测试数据,返回Mock方法的入参和预期返回对象,但问题仍未解决:
@RunWith(MockitoJUnitRunner.class) public class FooTest { @Mocked MockedObject mockedObject; @InjectMocks Foo underTest; @ParameterizedTest @MethodSource("dataProvider") public void test_ParametrizedTest(MockedInput mockedInput, Output expectedReturn) { // Given when(mockedObject.method(mockedInput)) .thenReturn(expectedReturn); // when val result = underTest.method(); // then assertEquals(expectedReturn.getCode(), result.getCode()); } private static Stream<Arguments> dataProvider() { return Stream.of( Arguments.of(mockedInput1, Output.builder().code(CodeEnum.S1).build()), Arguments.of(mockedInput2, Output.builder().code(CodeEnum.S2).build()) ); } }
提问
如何实现让Mock对象返回参数化测试中的参数?
解决方案
问题根源分析
- JUnit版本冲突:
@RunWith(MockitoJUnitRunner.class)是JUnit 4的运行器,和JUnit 5的@ParameterizedTest不兼容,这才是抛出匹配器异常的核心原因,和any()本身无关。 - MethodSource版本的额外问题:
dataProvider里的mockedInput1、mockedInput2是未初始化的变量,直接使用会导致对象为空,同时仍存在JUnit版本冲突的问题。 - 注解错误:普通Mockito用
@Mock注解,而非@Mocked(后者是PowerMock的注解)。
正确实现方式
方式一:保留枚举源,修复版本兼容问题
替换JUnit 4的运行器为JUnit 5的Mockito扩展,确保依赖使用JUnit Jupiter和Mockito的JUnit 5适配包:
@ExtendWith(MockitoExtension.class) public class FooTest { @Mock MockedObject mockedObject; @InjectMocks Foo underTest; @ParameterizedTest @EnumSource(CodeEnum.class) public void test_ParametrizedTest(CodeEnum enumParam) { Output expectedReturn = Output.builder().code(enumParam).build(); // Given when(mockedObject.method(any())) .thenReturn(expectedReturn); // when FooResult result = underTest.method(); // 若用lombok的val需确保依赖正确 // then assertEquals(enumParam, result.getCode()); } }
方式二:使用MethodSource,正确构造测试数据
同样替换为JUnit 5的扩展,并在dataProvider中初始化测试用的MockedInput对象:
@ExtendWith(MockitoExtension.class) public class FooTest { @Mock MockedObject mockedObject; @InjectMocks Foo underTest; @ParameterizedTest @MethodSource("dataProvider") public void test_ParametrizedTest(MockedInput mockedInput, Output expectedReturn) { // Given when(mockedObject.method(mockedInput)) .thenReturn(expectedReturn); // when FooResult result = underTest.method(); // then assertEquals(expectedReturn.getCode(), result.getCode()); } private static Stream<Arguments> dataProvider() { // 正确初始化MockedInput实例,比如用构造器或builder MockedInput input1 = new MockedInput("testInput1"); MockedInput input2 = new MockedInput("testInput2"); return Stream.of( Arguments.of(input1, Output.builder().code(CodeEnum.S1).build()), Arguments.of(input2, Output.builder().code(CodeEnum.S2).build()) ); } }
关键注意事项
- 必须统一使用JUnit 5的扩展机制,不能混用JUnit 4的
@RunWith和JUnit 5的参数化注解,否则会导致Mockito逻辑和参数化测试逻辑冲突。 - 确保Mockito注解正确,普通场景用
@Mock而非@Mocked。 MethodSource提供的测试数据必须是已正确初始化的对象,不能直接引用未赋值的变量。
内容的提问来源于stack exchange,提问作者PodGen4
相关产品推荐
相关产品推荐

