Mockito与PowerMock协同测试含私有void方法的Controller
使用Mockito和PowerMock为Controller编写单元测试问题
场景说明
我需要为如下Controller编写单元测试,该Controller包含一个带单个参数的私有void方法,依赖通过@Autowired注入的Service:
public class ProjectController { private ProjectServiceImpl service; @Autowired public void setInjectedBean(ProjectServiceImpl service) { this.service = service; } private void createProject(String someString) { // 调用service.doSomething(someString)处理逻辑 } }
ProjectServiceImpl标注了@Service注解,仅包含一个接收String参数的public void方法。
我的测试尝试
@RunWith(PowerMockRunner.class) @ExtendWith(MockitoExtension.class) @PrepareForTest({ProjectController.class, ProjectServiceImpl.class}) class ProjectControllerTest { @InjectMocks private ProjectController controller; @Mock private ProjectServiceImpl service; @Test void createProject() throws Exception { controller = PowerMockito.spy(new ProjectController()); PowerMockito.doNothing().when(controller, "createProject", new String("")); } }
问题
- PowerMock能否与Mockito协同工作?
- 使用PowerMock调用带参数的私有void方法的最恰当方式是什么?
重要提示:在提问前我特意查找了相关内容,未找到同时说明二者协同及PowerMock调用带参私有void方法的资料。
使用的依赖
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-core</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
问题解答
问题1:PowerMock能否与Mockito协同工作?
完全可以,但要注意版本匹配和正确配置:
- 你选用的
powermock-api-mockito2就是专门用于PowerMock与Mockito 2.x版本协同的依赖,这部分是正确的。 - 测试类同时使用
@RunWith(PowerMockRunner.class)和@ExtendWith(MockitoExtension.class)会冲突——JUnit 4用@RunWith,JUnit 5用@ExtendWith,二者选其一即可。你用的是JUnit 4的PowerMock依赖,所以要删掉@ExtendWith(MockitoExtension.class)。 - 依赖中重复引入了
powermock-module-junit4,可以删除重复的那一行。
问题2:使用PowerMock调用带参数的私有void方法的最恰当方式是什么?
你的核心需求应该是调用私有方法并验证其内部逻辑(比如是否正确调用了Service),而非mock私有方法(你的代码中doNothing().when(...)是在mock私有方法,这不是测试私有方法的正确姿势)。正确步骤如下:
正确测试流程
- 初始化Controller实例,注入mock的Service。
- 用PowerMock的
Whitebox.invokeMethod直接调用私有方法。 - 验证Service的方法是否被正确调用。
修正后的测试代码
@RunWith(PowerMockRunner.class) @PrepareForTest(ProjectController.class) // 仅需准备要测试的Controller类,Service不需要 class ProjectControllerTest { @Mock private ProjectServiceImpl service; private ProjectController controller; @Before public void setUp() { // 初始化mock MockitoAnnotations.openMocks(this); controller = new ProjectController(); // 注入mock的Service controller.setInjectedBean(service); } @Test void testCreateProject() throws Exception { String testParam = "test-project"; // 调用私有方法 Whitebox.invokeMethod(controller, "createProject", testParam); // 验证Service方法是否被正确调用 Mockito.verify(service).doSomething(testParam); } }
关键说明
Whitebox.invokeMethod是PowerMock专门用于调用私有方法的工具类,比原生反射更简洁易用。- 测试私有方法的意义在于验证其业务逻辑(比如是否正确调用依赖),而非mock它本身。
@PrepareForTest仅需包含要测试的Controller类,因为你没有mock Service的静态方法或构造方法,无需准备Service类。
依赖调整
删除重复的powermock-module-junit4依赖,并为cglib添加test scope,修正后的依赖如下:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-core</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> <scope>test</scope> </dependency>
内容的提问来源于stack exchange,提问作者music bot
相关产品推荐
相关产品推荐

