Azure Tables/Blobs测试遇final类无法Mock及Autowired空值问题
解决Azure Tables/Blobs客户端无法Mock及Autowired注入空值问题
一、Final类无法Mock的解决方案
Azure SDK中的TableServiceClient、TableClient(包括Blob相关类)都是final类,默认Mockito无法直接Mock,这里提供两种实用方案:
方案1:启用Mockito对Final类的支持
Mockito 2.10+版本支持Mock final类,只需两步配置:
- 在测试资源路径下创建文件
mockito-extensions/org.mockito.plugins.MockMaker,写入内容:mock-maker-inline - 正常使用Mockito创建Mock实例:
@Mock private TableServiceClient mockTableServiceClient; @Mock private TableClient mockTableClient;
测试时指定Mock行为,模拟表客户端获取场景:
when(mockTableServiceClient.getTableClient("Preferences")).thenReturn(mockTableClient);
方案2:封装自定义接口做依赖反转
如果不想依赖Mockito的特殊配置,推荐自己封装一层操作接口,让业务类依赖自定义接口而非SDK的final类:
// 自定义表操作接口 public interface AzureTableOperations { TableEntity getEntity(String partitionKey, String rowKey); void upsertEntity(TableEntity entity); } // 接口实现类,封装Azure SDK逻辑 @Service public class AzureTableOperationsImpl implements AzureTableOperations { private final TableClient tableClient; public AzureTableOperationsImpl(TableServiceClient tableServiceClient) { this.tableClient = tableServiceClient.getTableClient("Preferences"); } @Override public TableEntity getEntity(String partitionKey, String rowKey) { try { return tableClient.getEntity(partitionKey, rowKey); } catch (Exception e) { return null; } } @Override public void upsertEntity(TableEntity entity) { tableClient.upsertEntity(entity); } }
修改业务类,依赖自定义接口:
@Service public class AzureServicesTables implements PreferencesPersistenceService { private final AzureTableOperations tableOperations; private final String PARTITION_KEY = "PartitionKey"; @Autowired public AzureServicesTables(AzureTableOperations tableOperations) { this.tableOperations = tableOperations; } public String getUserPreferencesField(String user) { TableEntity entity = tableOperations.getEntity(PARTITION_KEY, user); if (entity == null) { entity = tableOperations.getEntity(PARTITION_KEY, "default"); } return String.valueOf(entity.getProperties().get("value")); } // savePreferences等方法同理修改,直接调用tableOperations方法 }
这样测试时可以轻松Mock自定义接口,完全避开final类限制。
二、Autowired注入空值的解决方法
测试时注入空值通常是因为没有正确处理依赖加载,分两种场景处理:
- 单元测试:不要用Autowired,手动构造业务类并传入Mock依赖:
@ExtendWith(MockitoExtension.class) public class AzureServicesTablesTest { @Mock private TableServiceClient mockTableServiceClient; @Mock private TableClient mockTableClient; private AzureServicesTables service; @BeforeEach void setUp() { service = new AzureServicesTables(mockTableServiceClient); when(mockTableServiceClient.getTableClient("Preferences")).thenReturn(mockTableClient); } // 测试逻辑... }
- 集成测试:添加
@SpringBootTest加载上下文,用@MockBean替换真实依赖为Mock实例:
@SpringBootTest public class AzureServicesTablesIntegrationTest { @MockBean private TableServiceClient mockTableServiceClient; @Autowired private AzureServicesTables service; @BeforeEach void setUp() { TableClient mockTableClient = Mockito.mock(TableClient.class); when(mockTableServiceClient.getTableClient("Preferences")).thenReturn(mockTableClient); // 配置mockTableClient的行为... } // 测试逻辑... }
三、模拟已创建表的测试示例
下面是单元测试的完整示例,模拟用户不存在时返回默认配置的场景:
@ExtendWith(MockitoExtension.class) public class AzureServicesTablesTest { @Mock private TableServiceClient mockTableServiceClient; @Mock private TableClient mockTableClient; private AzureServicesTables service; @BeforeEach void setUp() { service = new AzureServicesTables(mockTableServiceClient); when(mockTableServiceClient.getTableClient("Preferences")).thenReturn(mockTableClient); } @Test void getUserPreferencesField_UserNotFound_ReturnsDefault() { // 模拟用户查询抛出异常 when(mockTableClient.getEntity("PartitionKey", "testUser")).thenThrow(new ResourceNotFoundException("Not found")); // 模拟默认配置存在 TableEntity defaultEntity = new TableEntity("PartitionKey", "default"); defaultEntity.addProperty("value", "{\"theme\":\"dark\"}"); when(mockTableClient.getEntity("PartitionKey", "default")).thenReturn(defaultEntity); String result = service.getUserPreferencesField("testUser"); assertEquals("{\"theme\":\"dark\"}", result); verify(mockTableClient, times(1)).getEntity("PartitionKey", "testUser"); verify(mockTableClient, times(1)).getEntity("PartitionKey", "default"); } }
内容的提问来源于stack exchange,提问作者cdelrey
相关产品推荐
相关产品推荐

