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

Quarkus测试中如何注入Mock列表替换全部实现类

在Quarkus测试中完全替换@All注入的Bean集合

场景说明

现有业务Bean与接口定义如下:

MyService类:

@ApplicationScoped
public class MyService {
    @Inject
    @All
    List<MyDataProvider> dataProviders;

    public List<String> getAllData() {
        var data = new ArrayList<String>();

        for (var provider : dataProviders) {
            data.add(provider.getData());
        }

        return data;
    }
}

MyDataProvider接口:

public interface MyDataProvider {
    String getData();
}

当前存在数十个MyDataProvider的实现类,需要在单元测试中用指定的Mock集合完全替换这些真实实现,避免注入未Mock的Bean,仅测试基础功能。


解决方案

方法1:通过@Alternative + 自定义生产者替换集合

创建一个带@Alternative和高优先级的生产者Bean,直接生成你需要的Mock集合,让测试上下文优先使用这个集合而非所有真实实现:

@ApplicationScoped
@Alternative
@Priority(1) // 优先级高于所有真实Bean(默认真实Bean优先级为0)
public class MockDataProviderProducer {
    @Produces
    @All
    List<MyDataProvider> produceMockProviders() {
        // 定义你需要的Mock实例
        MyDataProvider mock1 = () -> "test-data-1";
        MyDataProvider mock2 = () -> "test-data-2";
        return List.of(mock1, mock2);
    }
}

在测试类中通过@AddBean将该生产者加入测试上下文:

@QuarkusTest
@AddBean(MockDataProviderProducer.class)
public class MyServiceTest {
    @Inject
    MyService myService;

    @Test
    void testGetAllData() {
        List<String> result = myService.getAllData();
        assertEquals(2, result.size());
        assertTrue(result.contains("test-data-1"));
        assertTrue(result.contains("test-data-2"));
    }
}

方法2:纯单元测试手动注入依赖

如果不需要启动Quarkus容器做集成测试,直接手动创建MyService实例并注入自定义Mock集合即可,无需依赖容器注入:

import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

public class MyServiceUnitTest {
    @Test
    void testGetAllData() {
        // 构建自定义Mock集合
        List<MyDataProvider> mockProviders = List.of(
            () -> "mock-value-1",
            () -> "mock-value-2"
        );

        // 初始化服务并注入依赖
        MyService myService = new MyService();
        try {
            Field field = MyService.class.getDeclaredField("dataProviders");
            field.setAccessible(true);
            field.set(myService, mockProviders);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            fail("Failed to inject mock providers", e);
        }

        // 执行测试断言
        List<String> result = myService.getAllData();
        assertEquals(2, result.size());
        assertEquals(List.of("mock-value-1", "mock-value-2"), result);
    }
}

方法3:使用@Mock替代所有实现类(批量Mock)

如果需要用Mockito生成Mock实例,可创建一个带@Mock的实现类,配合@Priority让它覆盖所有真实实现,再手动控制实例数量:

@Mock
@ApplicationScoped
@Priority(1)
public class MockedDataProvider implements MyDataProvider {
    @Override
    public String getData() {
        // 可通过Mockito.when()自定义返回值
        return "mocked-data";
    }
}

然后在测试类中,通过QuarkusMock安装多个实例:

@QuarkusTest
public class MyServiceTest {
    @Inject
    MyService myService;

    @BeforeEach
    void setupMocks() {
        // 创建多个Mock实例
        MyDataProvider mock1 = Mockito.mock(MyDataProvider.class);
        Mockito.when(mock1.getData()).thenReturn("mock-1");
        
        MyDataProvider mock2 = Mockito.mock(MyDataProvider.class);
        Mockito.when(mock2.getData()).thenReturn("mock-2");

        // 安装Mock,替换所有MyDataProvider实现
        QuarkusMock.installMockForType(mock1, MyDataProvider.class);
        QuarkusMock.installMockForType(mock2, MyDataProvider.class);
    }

    @Test
    void testGetAllData() {
        List<String> result = myService.getAllData();
        assertEquals(2, result.size());
        assertTrue(result.contains("mock-1"));
        assertTrue(result.contains("mock-2"));
    }
}

核心要点

  • @Alternative + @Priority是Quarkus中替换Bean的标准方式,高优先级的Bean会覆盖低优先级的真实实现。
  • 纯单元测试手动注入的方式无需启动容器,测试效率更高,适合简单逻辑验证。
  • 若使用Mockito,通过QuarkusMock.installMockForType可批量替换同类型的所有Bean实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:52:37