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

如何在JUnit测试中用Mockito Mock Google Guice依赖注入?

问题根源:Guice注入直接覆盖了Mockito的Mock

兄弟,你的Mock不生效的核心原因太明显了——在ErrorReproduction的start方法里,你手动创建了Guice Injector并调用injectMembers(this),这会直接覆盖Mockito通过@InjectMocks注入的dataCreation mock对象。当Verticle启动时,Guice会重新把真实的DataCreation实例塞进去,你之前用Mockito配置的mock行为自然就失效了。

解决方案1:重构Verticle,让依赖注入可控

最根本的解决办法是把Verticle里硬编码创建Injector的逻辑改掉,让外部能控制注入的依赖。这样测试时我们就能把mock对象塞进去,而不是让Guice自己创建真实对象。

修改Verticle代码

package com.testproject.starter.verticles;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;
import com.testproject.starter.model.DataCreationModel;
import com.testproject.starter.services.DataCreation;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

public class ErrorReproduction extends AbstractVerticle {
    @Inject
    @Named("dataCreation")
    DataCreation dataCreation;

    private final Injector injector;

    // 测试用构造函数:允许传入自定义Injector
    @Inject
    public ErrorReproduction(Injector injector) {
        this.injector = injector;
    }

    // 生产环境用无参构造函数,创建默认Injector
    public ErrorReproduction() {
        this(Guice.createInjector(new DataCreationModel()));
    }

    @Override
    public void start(Future<Void> startFuture) throws Exception {
        // 用传入的Injector完成注入,不再硬编码创建
        injector.injectMembers(this);
        int port = config().getInteger("http.port", 8080);
        Router router = Router.router(vertx);
        router.route("/api/getAll").handler(this::getAll);
        vertx.createHttpServer().requestHandler(router::accept)
             .listen(port, result -> startFuture.complete());
    }

    public void getAll(RoutingContext routingContext) {
        routingContext.response().putHeader("content-type", "application/json")
                      .end(Json.encodePrettily(dataCreation.getData().values()));
    }
}

修改测试代码

现在测试时我们可以创建一个包含mock对象的Guice Module,把它传入Verticle,这样Guice就会注入我们的mock而不是真实对象:

package com.testproject.starter.verticles;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.name.Names;
import com.testproject.starter.dao.DataCreationDao;
import com.testproject.starter.ppojo.Whisky;
import com.testproject.starter.services.DataCreation;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.mockito.Mockito.when;

@RunWith(VertxUnitRunner.class)
public class ErrorReproductionTest {
    Vertx vertx;
    int port;

    @Mock
    private DataCreation dataCreation;
    @Mock
    private DataCreationDao dataCreationDao;

    @Rule
    public RunTestOnContext rule = new RunTestOnContext();
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    private ErrorReproduction errVertical;

    @Before
    public void before(TestContext context) throws IOException {
        ServerSocket socket = new ServerSocket(0);
        port = socket.getLocalPort();
        socket.close();
        
        // 创建测试用的Guice Module,绑定我们的mock对象
        Injector testInjector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(DataCreation.class).annotatedWith(Names.named("dataCreation")).toInstance(dataCreation);
                bind(DataCreationDao.class).toInstance(dataCreationDao);
            }
        });
        
        // 用测试Injector创建Verticle
        errVertical = new ErrorReproduction(testInjector);
        
        DeploymentOptions options = new DeploymentOptions()
                .setConfig(new JsonObject().put("http.port", port));
        vertx = Vertx.vertx();
        rule.vertx().deployVerticle(errVertical, options, context.asyncAssertSuccess());
    }

    @After
    public void after(TestContext context) {
        vertx.close(context.asyncAssertSuccess());
    }

    @Test
    public void testGetAll(TestContext context){
        Map<Integer, Whisky> dataSets = new LinkedHashMap<>();
        Whisky w1 = new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay");
        Whisky w2 = new Whisky("Talisker 57° kya h", "Scotland, Island");
        Async async = context.async();
        dataSets.put(w1.getId(), w1);
        dataSets.put(w2.getId(), w2);

        // 配置mock的返回值
        when(dataCreationDao.getData()).thenReturn(dataSets);
        when(dataCreation.getData()).thenReturn(dataSets);

        HttpClient client = vertx.createHttpClient();
        client.getNow(port, "localhost", "/api/getAll", response -> {
            response.bodyHandler(body -> {
                // 这里可以加断言验证返回结果是否符合预期
                context.assertEquals(Json.encodePrettily(dataSets.values()), body.toString());
                client.close();
                async.complete();
            });
        });
    }
}

解决方案2:用Mockito-Guice简化测试(可选)

如果你不想大改Verticle的构造函数,可以用mockito-guice扩展,它能让Guice直接使用Mockito的mock对象。首先加个Maven依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-guice</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

然后测试时可以直接用@Inject和Mockito的mock结合,不过本质上还是需要让Verticle使用测试用的Guice Module,核心思路和方案1一致,只是写法更简洁。

关键提醒

  • 别在Verticle里硬编码创建Injector:这是测试的大忌,会把依赖写死,完全失去了依赖注入的灵活性。
  • Guice注入优先级高于Mockito:只要你调用了Guice的injectMembers,之前Mockito注入的对象就会被覆盖,这点一定要记住。
  • 测试环境用专门的Module:测试时一定要用测试Module绑定mock,和生产环境的Module隔离开,避免真实对象干扰测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:37:33