如何使用Mockito对含JGit操作的静态方法编写单元测试?
解决方案
1. 前置依赖准备
需要引入PowerMock与Mockito整合的测试依赖,以下是常用的Maven依赖坐标(请根据你使用的Mockito版本选择适配的PowerMock版本):
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.12.4</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>
2. 完整测试代码实现
假设待测试的静态方法存放在GitUtils类中,测试代码如下:
import org.eclipse.jgit.api.AddCommand; import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.lib.ObjectId; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; @RunWith(PowerMockRunner.class) @PrepareForTest({GitUtils.class, Git.class, Paths.class, ObjectId.class}) public class GitUtilsTest { @Mock private Git mockGit; @Mock private CheckoutCommand mockCheckoutCommand; @Mock private AddCommand mockAddCommand; @Mock private CommitCommand mockCommitCommand; @Mock private RevCommit mockRevCommit; @Mock private ObjectId mockObjectId; @Test public void testAddAndCommitUntrackedChanges() throws Exception { // 1. 构造测试参数 File testGitFile = new File("/tmp/PackageName/.git"); String testBranch = "test/feature-001"; String testCommitMsg = "test commit"; List<String> testFiles = Arrays.asList( "/tmp/PackageName/a.txt", "/tmp/PackageName/src/b.java" ); String expectedCommitId = "abc123def456"; // 2. Stub静态方法调用 PowerMockito.mockStatic(Git.class); when(Git.open(testGitFile)).thenReturn(mockGit); PowerMockito.mockStatic(Paths.class); // 处理第一个文件的Path构造 Path pathA = Paths.get("/tmp/PackageName/a.txt"); Path basePath = Paths.get("/", "tmp", "PackageName"); Path relativeA = basePath.relativize(pathA); when(Paths.get("/tmp/PackageName/a.txt")).thenReturn(pathA); // 处理第二个文件的Path构造 Path pathB = Paths.get("/tmp/PackageName/src/b.java"); Path relativeB = basePath.relativize(pathB); when(Paths.get("/tmp/PackageName/src/b.java")).thenReturn(pathB); // Stub公共基础路径构造 when(Paths.get("/", "tmp", "PackageName")).thenReturn(basePath); PowerMockito.mockStatic(ObjectId.class); when(ObjectId.toString(mockObjectId)).thenReturn(expectedCommitId); // 3. Stub JGit命令链式调用 when(mockGit.checkout()).thenReturn(mockCheckoutCommand); when(mockCheckoutCommand.setCreateBranch(true)).thenReturn(mockCheckoutCommand); when(mockCheckoutCommand.setName(testBranch)).thenReturn(mockCheckoutCommand); doNothing().when(mockCheckoutCommand).call(); when(mockGit.add()).thenReturn(mockAddCommand); when(mockAddCommand.addFilepattern(relativeA.toString())).thenReturn(mockAddCommand); when(mockAddCommand.addFilepattern(relativeB.toString())).thenReturn(mockAddCommand); doNothing().when(mockAddCommand).call(); when(mockGit.commit()).thenReturn(mockCommitCommand); when(mockCommitCommand.setMessage(testCommitMsg)).thenReturn(mockCommitCommand); when(mockCommitCommand.call()).thenReturn(mockRevCommit); when(mockRevCommit.getId()).thenReturn(mockObjectId); // 4. 调用待测试静态方法 String actualCommitId = GitUtils.addAndCommitUntrackedChanges(testGitFile, testBranch, testCommitMsg, testFiles); // 5. 验证结果与调用行为 assertEquals(expectedCommitId, actualCommitId); verify(mockCheckoutCommand, times(1)).setCreateBranch(true); verify(mockCheckoutCommand, times(1)).setName(testBranch); verify(mockAddCommand, times(1)).addFilepattern("a.txt"); verify(mockAddCommand, times(1)).addFilepattern("src/b.java"); verify(mockCommitCommand, times(1)).setMessage(testCommitMsg); } }
3. 关键实现说明
- 静态方法Mock:所有调用了静态方法的类都需要放到
@PrepareForTest注解中,由PowerMock代理类加载器加载,才能完成静态方法的Stub。 - 链式调用处理:JGit的操作命令都是链式调用设计,每次调用set方法都需要Stub返回命令实例本身,避免调用过程中出现空指针。
- 路径处理验证:通过Mock Paths.get的返回值,就可以验证待测试方法中相对路径计算的逻辑是否正确,不需要真实创建测试文件。
如果你使用JUnit5,PowerMock适配支持有限,可以改用Mockito 3.4及以上版本提供的
mockStatic方法实现静态方法Mock,不需要引入PowerMock依赖。
内容的提问来源于stack exchange,提问作者SATYAM MITTAL 17103094
相关产品推荐
相关产品推荐

