如何用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
相关产品推荐
相关产品推荐

