如何用Mockito与JUnit5编写BigQuery的JUnit测试并Mock/初始化Table
问题:Mockito+JUnit5测试BigQuery Table类Mock/初始化失败
我尝试使用Mockito和JUnit5编写BigQuery的JUnit测试用例,但无法Mock或初始化Table类,请问有可行的解决方法吗?
业务代码
private JsonStreamWriter streamWriter; void WriteToBQ(TableName parentTable) { BigQuery bigquery = BigQueryOptions .newBuilder() .setProjectId(AppConstants.PROJECT_ID) .build() .getService(); Table table = bigquery.getTable(parentTable.getDataset(), parentTable.getTable()); Schema schema = table.getDefinition().getSchema(); TableSchema tableSchema = BqToBqStorageSchemaConverter.convertTableSchema(schema); streamWriter = JsonStreamWriter.newBuilder(parentTable.toString(), tableSchema).build(); }
我尝试过的方法
- 直接实例化Table失败
尝试通过以下代码初始化Table,但TableInfo.BuilderImpl是非公共类,无法跨包使用:
table = new Table(bigquery, new TableInfo.BuilderImpl(TABLE_INFO));
- Mock Table类未成功
编写了以下Mock代码,但没有效果:
bigquery = mock(BigQuery.class); mockOptions = mock(BigQueryOptions.class); table = mock(Table.class); when(bigquery.getTable(any(),any())).thenReturn(table); when(bigquery.getOptions()).thenReturn(mockOptions);
解决方案
1. 先重构业务代码(关键步骤)
问题根源是业务方法内直接创建BigQuery实例,导致测试时无法替换为Mock对象。需要把BigQuery的创建逻辑抽离,改为依赖注入:
重构后的业务代码示例(构造注入方式)
private JsonStreamWriter streamWriter; private final BigQuery bigquery; // 生产环境传入真实BigQuery实例,测试时传入Mock实例 public YourServiceClass(BigQuery bigquery) { this.bigquery = bigquery; } void WriteToBQ(TableName parentTable) { Table table = bigquery.getTable(parentTable.getDataset(), parentTable.getTable()); Schema schema = table.getDefinition().getSchema(); TableSchema tableSchema = BqToBqStorageSchemaConverter.convertTableSchema(schema); streamWriter = JsonStreamWriter.newBuilder(parentTable.toString(), tableSchema).build(); }
如果不想修改构造方法,也可以用工厂方法抽离:
private JsonStreamWriter streamWriter; // 默认返回真实BigQuery,测试时可重写该方法 protected BigQuery createBigQuery() { return BigQueryOptions .newBuilder() .setProjectId(AppConstants.PROJECT_ID) .build() .getService(); } void WriteToBQ(TableName parentTable) { BigQuery bigquery = createBigQuery(); // 调用工厂方法获取实例 // 后续逻辑不变... }
2. 编写正确的测试用例
现在可以Mock所有依赖对象,让测试逻辑正常执行:
测试代码示例
import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.junit.jupiter.api.extension.ExtendWith; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class YourServiceClassTest { @Mock private BigQuery mockBigQuery; @Mock private Table mockTable; @Mock private TableDefinition mockTableDefinition; @Mock private Schema mockSchema; @Mock private JsonStreamWriter mockStreamWriter; // 构造注入方式初始化测试对象 private YourServiceClass service = new YourServiceClass(mockBigQuery); // 工厂方法方式的初始化(如果用工厂方法重构) /* private YourServiceClass service = new YourServiceClass() { @Override protected BigQuery createBigQuery() { return mockBigQuery; } }; */ @Test void testWriteToBQ() { // 测试用TableName TableName testTable = TableName.of("test-project", "test-dataset", "test-table"); // 依次Stub各层级方法的返回值 when(mockBigQuery.getTable(eq(testTable.getDataset()), eq(testTable.getTable()))) .thenReturn(mockTable); when(mockTable.getDefinition()).thenReturn(mockTableDefinition); when(mockTableDefinition.getSchema()).thenReturn(mockSchema); // 按需Mock转换逻辑和Writer构建 TableSchema mockTableSchema = mock(TableSchema.class); when(BqToBqStorageSchemaConverter.convertTableSchema(mockSchema)) .thenReturn(mockTableSchema); when(JsonStreamWriter.newBuilder(eq(testTable.toString()), eq(mockTableSchema))) .thenReturn(mock(JsonStreamWriter.Builder.class)); when(JsonStreamWriter.newBuilder(anyString(), any(TableSchema.class)).build()) .thenReturn(mockStreamWriter); // 执行测试方法 service.WriteToBQ(testTable); // 验证关键方法的调用 verify(mockBigQuery).getTable(eq(testTable.getDataset()), eq(testTable.getTable())); verify(mockTable).getDefinition(); verify(mockTableDefinition).getSchema(); } }
为什么之前的Mock没生效?
原来的业务代码在方法内部直接new了BigQuery实例,完全没有用到测试代码中创建的mockBigQuery对象,所以所有Stub设置都不会生效。重构后让业务代码依赖外部传入的BigQuery,测试时就能替换为Mock对象了。
内容的提问来源于stack exchange,提问作者Harmeet Kaur
相关产品推荐
相关产品推荐

