如何使用JUnit Mock Java中Builder创建的SymphonyClient对象
嗨,这个场景我太熟悉了!你现在的问题在于doSomething()方法里直接new了SymphonyClient.Builder,这种硬编码的实例化会让单元测试很难Mock——毕竟Mockito默认没法Mock方法内部的构造调用。下面给你几种可行的方案,按推荐程度排序:
方案1:重构代码,用依赖注入(最推荐)
这是最符合SOLID设计原则的做法,把SymphonyClient(或者它的Builder)作为依赖注入到你的类里,而不是在方法内部直接创建。
方式A:直接注入SymphonyClient
修改你的业务类,通过构造方法传入SymphonyClient:
public class YourBusinessClass { private final SymphonyClient symphonyClient; private final String message; // 假设message是类的成员变量 // 构造注入:生产环境传真实实例,测试传Mock实例 public YourBusinessClass(SymphonyClient symphonyClient, String message) { this.symphonyClient = symphonyClient; this.message = message; } public void doSomething() { symphonyClient.postMessage(message); } }
对应的单元测试代码(用Mockito):
import org.junit.Test; import org.mockito.Mockito; public class YourBusinessClassTest { @Test public void testDoSomething() { // 1. Mock SymphonyClient对象 SymphonyClient mockClient = Mockito.mock(SymphonyClient.class); String testMessage = "test message"; // 2. 初始化被测试类,传入Mock对象 YourBusinessClass testedClass = new YourBusinessClass(mockClient, testMessage); // 3. 执行待测试方法 testedClass.doSomething(); // 4. 验证postMessage是否被正确调用 Mockito.verify(mockClient).postMessage(testMessage); } }
方式B:注入SymphonyClient.Builder(更灵活的场景)
如果你的SymphonyClient需要每次调用都新建实例,那可以注入Builder而不是Client本身:
public class YourBusinessClass { private final SymphonyClient.Builder clientBuilder; private final String message; public YourBusinessClass(SymphonyClient.Builder clientBuilder, String message) { this.clientBuilder = clientBuilder; this.message = message; } public void doSomething() { SymphonyClient client = clientBuilder.build(); client.postMessage(message); } }
测试代码:
import org.junit.Test; import org.mockito.Mockito; public class YourBusinessClassTest { @Test public void testDoSomething() { // Mock Client和Builder SymphonyClient mockClient = Mockito.mock(SymphonyClient.class); SymphonyClient.Builder mockBuilder = Mockito.mock(SymphonyClient.Builder.class); // 让Builder的build()返回Mock的Client Mockito.when(mockBuilder.build()).thenReturn(mockClient); String testMessage = "test message"; YourBusinessClass testedClass = new YourBusinessClass(mockBuilder, testMessage); testedClass.doSomething(); // 验证Builder的build()和Client的postMessage都被调用 Mockito.verify(mockBuilder).build(); Mockito.verify(mockClient).postMessage(testMessage); } }
方案2:抽取工厂方法(轻量折中方案)
如果不想大改类的依赖结构,可以把创建SymphonyClient的逻辑抽成一个受保护的方法,然后在测试中用Mockito的spy来替换这个方法的返回值。
修改业务类:
public class YourBusinessClass { private final String password; private final String uname; private final String message; public YourBusinessClass(String password, String uname, String message) { this.password = password; this.uname = uname; this.message = message; } public void doSomething() { SymphonyClient client = createSymphonyClient(); client.postMessage(message); } // 抽取工厂方法,protected权限方便测试重写 protected SymphonyClient createSymphonyClient() { // 注意:你原来的代码漏了调用Builder的build()方法,这里补上 return new SymphonyClient.Builder("", password, uname, "").build(); } }
测试代码:
import org.junit.Test; import org.mockito.Mockito; public class YourBusinessClassTest { @Test public void testDoSomething() { // 创建业务类的spy对象(保留真实逻辑,只替换特定方法) YourBusinessClass testedClass = Mockito.spy(new YourBusinessClass("testPass", "testUname", "testMsg")); // Mock SymphonyClient SymphonyClient mockClient = Mockito.mock(SymphonyClient.class); // 让spy对象调用createSymphonyClient时返回Mock实例 Mockito.doReturn(mockClient).when(testedClass).createSymphonyClient(); // 执行测试 testedClass.doSomething(); // 验证方法调用 Mockito.verify(mockClient).postMessage("testMsg"); } }
方案3:用PowerMock Mock构造方法(不推荐,万不得已才用)
如果完全不想修改业务代码,只能用PowerMock来Mock内部的构造调用,但这种方式会绕过Java的正常反射机制,增加测试的复杂度和维护成本,尽量避免。
首先需要引入PowerMock的依赖,然后编写测试:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) // 告诉PowerMock要准备测试的类(这里是SymphonyClient.Builder) @PrepareForTest(SymphonyClient.Builder.class) public class YourBusinessClassTest { @Test public void testDoSomething() throws Exception { // Mock SymphonyClient SymphonyClient mockClient = Mockito.mock(SymphonyClient.class); // Mock Builder的构造方法,返回Mock的Builder实例 SymphonyClient.Builder mockBuilder = Mockito.mock(SymphonyClient.Builder.class); PowerMockito.whenNew(SymphonyClient.Builder.class) .withArguments(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()) .thenReturn(mockBuilder); // 让Mock Builder的build()返回Mock Client Mockito.when(mockBuilder.build()).thenReturn(mockClient); YourBusinessClass testedClass = new YourBusinessClass("testPass", "testUname", "testMsg"); testedClass.doSomething(); // 验证调用 Mockito.verify(mockClient).postMessage("testMsg"); } }
总的来说,优先选择依赖注入或者工厂方法的方案,PowerMock只作为最后的应急手段。
内容的提问来源于stack exchange,提问作者station
相关产品推荐
相关产品推荐

