You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试内部通过new创建对象的方法?Mock FooWrapper实现测试

问题描述

需要测试getFoolList方法,该方法内部通过new直接创建FooWrapper实例。希望Mock FooWrapper,让wrapper.connect()不执行实际操作,调用wrapper.retrieveFileBytes(path.replace("somePath", ""))时返回指定byte[],询问是否有可行的实现方式。

原方法代码

@Override
public List<Foo> getFoolList(String path) {
    try (FooWrapper wrapper = new FooWrapper(
            host, port, login, password, logging, connectionTimeOut)
    ) {
        wrapper.connect();
        byte[] archiveBytes = wrapper.retrieveFileBytes(path.replace("somePath", ""));
       // some logic to process archiveBytes and return List<Foo>
    } catch (Exception e) {
        // exception handling
    }
}

预期测试代码示例

FooWrapper wrapper = Mockito.mock(FooWrapper.class);
when(wrapper.connect()).thenDoNothing();
when(wrapper.retrieveFileBytes(any())).thenReturn(someBytes);

// 调用getFoolList并断言
assertTrue(getFooList("test").get(0) instanceof Foo);
可行实现方案

直接在方法内new对象会导致无法直接用Mockito mock,因为Mockito只能处理被注入或通过工厂获取的实例。以下是几种可行的改造+测试方案:

方案1:重构代码,依赖注入FooWrapper(推荐)

最合理的方式是抽离FooWrapper的创建逻辑,通过依赖注入的方式传入,测试时直接注入mock实例。

重构后的原代码

public class FooService {
    private final FooWrapperFactory wrapperFactory;
    // 其他字段:host, port, login等

    // 构造器注入工厂
    public FooService(FooWrapperFactory wrapperFactory, String host, int port, String login, String password, boolean logging, int connectionTimeOut) {
        this.wrapperFactory = wrapperFactory;
        // 初始化其他字段
    }

    @Override
    public List<Foo> getFoolList(String path) {
        try (FooWrapper wrapper = wrapperFactory.create(host, port, login, password, logging, connectionTimeOut)) {
            wrapper.connect();
            byte[] archiveBytes = wrapper.retrieveFileBytes(path.replace("somePath", ""));
            // some logic
        }
    }
}

// 定义工厂接口
public interface FooWrapperFactory {
    FooWrapper create(String host, int port, String login, String password, boolean logging, int connectionTimeOut);
}

// 生产环境的工厂实现
public class DefaultFooWrapperFactory implements FooWrapperFactory {
    @Override
    public FooWrapper create(String host, int port, String login, String password, boolean logging, int connectionTimeOut) {
        return new FooWrapper(host, port, login, password, logging, connectionTimeOut);
    }
}

测试代码

// mock工厂和Wrapper实例
FooWrapperFactory mockFactory = Mockito.mock(FooWrapperFactory.class);
FooWrapper mockWrapper = Mockito.mock(FooWrapper.class);
when(mockFactory.create(anyString(), anyInt(), anyString(), anyString(), anyBoolean(), anyInt())).thenReturn(mockWrapper);

// 配置mock行为
when(mockWrapper.connect()).thenDoNothing();
byte[] testBytes = new byte[]{1,2,3};
when(mockWrapper.retrieveFileBytes(anyString())).thenReturn(testBytes);

// 创建被测服务并注入mock工厂
FooService service = new FooService(mockFactory, "testHost", 1234, "testLogin", "testPass", true, 1000);
// 调用方法并断言
List<Foo> result = service.getFoolList("somePath/test");
assertTrue(result.get(0) instanceof Foo);

方案2:使用PowerMock mock new操作(临时方案)

如果无法重构原代码,可以用PowerMock拦截new操作返回mock实例,但该方案会修改字节码,增加测试复杂度,长期维护性差。

测试代码(JUnit4 + PowerMock)

@RunWith(PowerMockRunner.class)
@PrepareForTest(FooService.class) // 指定需要mock new操作的类
public class FooServiceTest {

    @Test
    public void testGetFoolList() throws Exception {
        // 创建mock Wrapper并配置行为
        FooWrapper mockWrapper = Mockito.mock(FooWrapper.class);
        when(mockWrapper.connect()).thenDoNothing();
        byte[] testBytes = new byte[]{1,2,3};
        when(mockWrapper.retrieveFileBytes(anyString())).thenReturn(testBytes);

        // 拦截new FooWrapper(...)调用,返回mock实例
        PowerMockito.whenNew(FooWrapper.class)
                .withArguments(anyString(), anyInt(), anyString(), anyString(), anyBoolean(), anyInt())
                .thenReturn(mockWrapper);

        // 创建被测服务并测试
        FooService service = new FooService("testHost", 1234, "testLogin", "testPass", true, 1000);
        List<Foo> result = service.getFoolList("somePath/test");
        assertTrue(result.get(0) instanceof Foo);
    }
}

方案3:抽离创建逻辑为私有方法(折衷方案)

若不想引入工厂类,可把FooWrapper的创建逻辑抽成私有方法,再用Mockito的spy mock这个方法。

重构后的原代码

public class FooService {
    // 其他字段:host, port等

    @Override
    public List<Foo> getFoolList(String path) {
        try (FooWrapper wrapper = createFooWrapper()) {
            wrapper.connect();
            byte[] archiveBytes = wrapper.retrieveFileBytes(path.replace("somePath", ""));
            // some logic
        }
    }

    // 抽离创建逻辑为私有方法
    private FooWrapper createFooWrapper() {
        return new FooWrapper(host, port, login, password, logging, connectionTimeOut);
    }
}

测试代码

@Test
public void testGetFoolList() throws Exception {
    // spy被测对象,保留原有逻辑但可mock私有方法
    FooService service = Mockito.spy(new FooService("testHost", 1234, "testLogin", "testPass", true, 1000));
    FooWrapper mockWrapper = Mockito.mock(FooWrapper.class);

    // mock私有方法返回mock实例
    PowerMockito.doReturn(mockWrapper).when(service, "createFooWrapper");

    // 配置mock行为
    when(mockWrapper.connect()).thenDoNothing();
    byte[] testBytes = new byte[]{1,2,3};
    when(mockWrapper.retrieveFileBytes(anyString())).thenReturn(testBytes);

    // 调用并断言
    List<Foo> result = service.getFoolList("somePath/test");
    assertTrue(result.get(0) instanceof Foo);
}

内容的提问来源于stack exchange,提问作者user19282140

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 21:05:38