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

如何用JUnit+PowerMockito编写Vertx数据库类测试用例实现80%覆盖率

Vertx Verticle测试方案(JUnit + PowerMockito)

一、EmployeeDBVerticle 测试

核心要Mock的是JDBCPool及其数据库操作逻辑,避免依赖真实数据库。以下是完整测试用例:

import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.JDBCPool;
import io.vertx.sqlclient.PreparedQuery;
import io.vertx.sqlclient.RowSet;
import io.vertx.sqlclient.Tuple;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest({JDBCPool.class}) // 标记需要静态Mock的类
public class EmployeeDBVerticleTest {

    private EmployeeDBVerticle dbVerticle;
    private Vertx mockVertx;
    private JDBCPool mockJdbcPool;
    private PreparedQuery<RowSet> mockPreparedQuery;
    private RowSet mockRowSet;

    @Before
    public void setUp() {
        // 初始化Mock对象
        mockVertx = mock(Vertx.class);
        mockJdbcPool = mock(JDBCPool.class);
        mockPreparedQuery = mock(PreparedQuery.class);
        mockRowSet = mock(RowSet.class);

        // 静态Mock JDBCPool.pool方法,返回mock的连接池
        PowerMockito.mockStatic(JDBCPool.class);
        when(JDBCPool.pool(eq(mockVertx), any(JsonObject.class))).thenReturn(mockJdbcPool);

        // 初始化Verticle并绑定mock的Vertx
        dbVerticle = new EmployeeDBVerticle();
        dbVerticle.setVertx(mockVertx);
    }

    @Test
    public void testStartSuccess() throws Exception {
        // 启动Verticle,验证EventBus消费者是否注册成功
        dbVerticle.start();
        verify(mockVertx).eventBus().consumer(eq(EventBusAddresses.ADDRESS_SAVE), any());
    }

    @Test
    public void testSaveStudentSuccess() {
        // 模拟SQL执行成功,且影响行数>0
        when(mockJdbcPool.preparedQuery(eq(SQLConstants.INSERT_EMPLOYEE))).thenReturn(mockPreparedQuery);
        when(mockPreparedQuery.execute(eq(Tuple.of("1", "Alice")))).thenReturn(mockRowSet);
        when(mockRowSet.rowCount()).thenReturn(1);

        JsonObject student = new JsonObject().put("id", "1").put("name", "Alice");
        dbVerticle.saveStudent(student)
                .onSuccess(result -> {
                    // 验证成功结果
                    assert result.equals("Data Saved Successfully");
                });
    }

    @Test
    public void testSaveStudentFailWhenNoRowsAffected() {
        // 模拟SQL执行成功,但无影响行数
        when(mockJdbcPool.preparedQuery(eq(SQLConstants.INSERT_EMPLOYEE))).thenReturn(mockPreparedQuery);
        when(mockPreparedQuery.execute(eq(Tuple.of("2", "Bob")))).thenReturn(mockRowSet);
        when(mockRowSet.rowCount()).thenReturn(0);

        JsonObject student = new JsonObject().put("id", "2").put("name", "Bob");
        dbVerticle.saveStudent(student)
                .onFailure(cause -> {
                    // 验证失败原因
                    assert cause.getMessage().equals("Error occurred");
                });
    }

    @Test
    public void testSaveStudentFailWhenSqlError() {
        // 模拟SQL执行失败
        when(mockJdbcPool.preparedQuery(eq(SQLConstants.INSERT_EMPLOYEE))).thenReturn(mockPreparedQuery);
        when(mockPreparedQuery.execute(eq(Tuple.of("3", "Charlie")))).thenThrow(new RuntimeException("DB error"));

        JsonObject student = new JsonObject().put("id", "3").put("name", "Charlie");
        dbVerticle.saveStudent(student)
                .onFailure(cause -> {
                    // 验证失败原因
                    assert cause.getMessage().equals("Failed");
                });
    }

    @Test
    public void testEventBusConsumerSuccess() throws Exception {
        // 启动Verticle后,模拟EventBus消息接收
        dbVerticle.start();
        Message<JsonObject> mockMessage = mock(Message.class);
        JsonObject student = new JsonObject().put("id", "4").put("name", "David");

        // 模拟saveStudent返回成功
        when(mockJdbcPool.preparedQuery(eq(SQLConstants.INSERT_EMPLOYEE))).thenReturn(mockPreparedQuery);
        when(mockPreparedQuery.execute(eq(Tuple.of("4", "David")))).thenReturn(mockRowSet);
        when(mockRowSet.rowCount()).thenReturn(1);

        // 执行saveStudent并验证reply逻辑
        dbVerticle.saveStudent(student).onSuccess(s -> {
            verify(mockMessage).reply(eq("Data Saved Successfully"));
        });
    }
}

关键说明:

  • 使用@PrepareForTest标记JDBCPool,才能Mock其静态方法pool
  • Mock PreparedQuery和RowSet模拟数据库的三种执行结果:成功、无影响行、执行异常
  • 验证EventBus消费者注册,以及消息处理后的reply/fail逻辑

二、EmployeeHttpVerticle 测试

核心要Mock的是Vertx、Router、EventBus和RoutingContext,模拟HTTP请求与EventBus交互。以下是完整测试用例:

import io.vertx.core.Vertx;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Reply;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Router.class, BodyHandler.class})
public class EmployeeHttpVerticleTest {

    private EmployeeHttpVerticle httpVerticle;
    private Vertx mockVertx;
    private Router mockRouter;
    private HttpServer mockHttpServer;
    private EventBus mockEventBus;
    private RoutingContext mockRoutingContext;

    @Before
    public void setUp() {
        // 初始化Mock对象
        mockVertx = mock(Vertx.class);
        mockRouter = mock(Router.class);
        mockHttpServer = mock(HttpServer.class);
        mockEventBus = mock(EventBus.class);
        mockRoutingContext = mock(RoutingContext.class);

        // 静态Mock Router.router方法
        PowerMockito.mockStatic(Router.class);
        when(Router.router(eq(mockVertx))).thenReturn(mockRouter);

        // 静态Mock BodyHandler.create方法
        PowerMockito.mockStatic(BodyHandler.class);
        when(BodyHandler.create()).thenReturn(mock(BodyHandler.class));

        // 模拟Vertx的方法返回
        when(mockVertx.createHttpServer()).thenReturn(mockHttpServer);
        when(mockVertx.eventBus()).thenReturn(mockEventBus);
        when(mockHttpServer.requestHandler(any())).thenReturn(mockHttpServer);

        // 初始化Verticle并绑定mock的Vertx
        httpVerticle = new EmployeeHttpVerticle();
        httpVerticle.setVertx(mockVertx);
    }

    @Test
    public void testStartSuccess() throws Exception {
        // 启动Verticle,验证Router配置和HttpServer启动
        httpVerticle.start();
        verify(mockRouter).route().handler(any(BodyHandler.class));
        verify(mockRouter).route(eq(HttpMethod.POST), eq("/save/employee")).handler(any());
        verify(mockHttpServer).listen(eq(8090));
    }

    @Test
    public void testSaveStudentSuccess() {
        // 模拟请求体和EventBus回复成功
        JsonObject student = new JsonObject().put("id", "1").put("name", "Alice");
        when(mockRoutingContext.getBodyAsJson()).thenReturn(student);

        Reply mockReply = mock(Reply.class);
        when(mockReply.succeeded()).thenReturn(true);
        when(mockReply.result().body()).thenReturn("Data Saved Successfully");

        // 模拟EventBus.request的异步回调触发
        doAnswer(invocation -> {
            Runnable callback = invocation.getArgument(2);
            callback.run();
            return null;
        }).when(mockEventBus).request(eq(EventBusAddresses.ADDRESS_SAVE), eq(student), any());

        // 执行saveStudent方法
        httpVerticle.saveStudent(mockRoutingContext);

        // 验证响应和第三方调用
        verify(mockRoutingContext.response()).end(eq("Data Saved Successfully"));
        verify(mockEventBus).request(eq(EventBusAddresses.ADDRESS_SAVE), eq(student));
    }

    @Test
    public void testSaveStudentFail() {
        // 模拟请求体和EventBus回复失败
        JsonObject student = new JsonObject().put("id", "2").put("name", "Bob");
        when(mockRoutingContext.getBodyAsJson()).thenReturn(student);

        Reply mockReply = mock(Reply.class);
        when(mockReply.succeeded()).thenReturn(false);
        when(mockReply.cause().getMessage()).thenReturn("Save failed");

        // 模拟EventBus.request的异步回调触发
        doAnswer(invocation -> {
            Runnable callback = invocation.getArgument(2);
            callback.run();
            return null;
        }).when(mockEventBus).request(eq(EventBusAddresses.ADDRESS_SAVE), eq(student), any());

        // 执行saveStudent方法
        httpVerticle.saveStudent(mockRoutingContext);

        // 验证错误响应
        verify(mockRoutingContext.response()).end(eq("Save failed"));
        verify(mockEventBus, never()).request(eq(EventBusAddresses.ADDRESS_SAVE), eq(student)); // 第三方调用不会触发
    }

    @Test
    public void testCallThirdParty() {
        JsonObject student = new JsonObject().put("id", "3").put("name", "Charlie");
        httpVerticle.callThirdParty(student);
        verify(mockEventBus).request(eq(EventBusAddresses.ADDRESS_SAVE), eq(student));
    }
}

关键说明:

  • Mock Router和BodyHandler的静态创建方法,避免依赖真实路由对象
  • 使用doAnswer模拟EventBus异步回调的执行,触发请求处理逻辑
  • 验证HTTP响应内容,以及callThirdParty方法的触发条件(成功时执行,失败时不执行)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:25:06