Android测试替身:Fake、Mock与Stub实战

Android测试替身:Fake、Mock与Stub实战

【免费下载链接】awesome-android A curated list of awesome Android packages and resources. 【免费下载链接】awesome-android 项目地址: https://gitcode.com/gh_mirrors/awe/awesome-android

你是否还在为Android单元测试中的外部依赖烦恼?当数据库、网络请求或第三方API成为测试障碍时,测试替身(Test Double)就是你的解决方案。本文将通过具体场景和代码示例,带你掌握Fake、Mock与Stub的实战应用,让你的测试代码更稳定、执行速度提升40%以上。读完本文你将能够:区分三种测试替身的适用场景、使用Roboletric框架实现本地单元测试、通过AssertJ Android编写可读性强的断言。

测试替身三兄弟:定义与选型指南

测试替身是替代真实组件的特殊对象,根据行为和用途可分为三类:

类型核心功能典型应用场景实现难度
Stub(存根)提供预设响应,不验证交互模拟网络请求返回固定JSON简单
Mock(模拟)验证交互行为(如方法调用次数)确认按钮点击事件是否触发中等
Fake(伪对象)实现简化版业务逻辑内存数据库替代真实SQLite复杂

测试替身关系

选择策略:当需要验证"是否调用"时用Mock,需要"返回特定数据"时用Stub,而当测试需要"真实逻辑但简化实现"时选择Fake。例如在登录功能测试中,用Fake实现内存缓存、Mock验证token存储逻辑、Stub模拟失效的网络连接。

从零实现Fake:内存数据库实战

以用户资料缓存为例,使用Fake替代Room数据库。首先创建实现相同接口的内存版本:

public class FakeUserDao implements UserDao {
    private final Map<String, User> inMemoryDb = new HashMap<>();
    
    @Override
    public void save(User user) {
        inMemoryDb.put(user.id, user);
    }
    
    @Override
    public User findById(String id) {
        return inMemoryDb.get(id);
    }
}

在测试中注入Fake替代真实DAO:

@Test
public void testUserCache() {
    UserRepository repo = new UserRepository(new FakeUserDao());
    repo.saveUser(new User("1", "Test"));
    
    // 使用[AssertJ Android](https://github.com/square/assertj-android)断言
    assertThat(repo.getUser("1").getName()).isEqualTo("Test");
}

Mockito模拟网络交互

验证登录流程中API调用的正确性,使用Mockito创建Mock对象:

@RunWith(RobolectricTestRunner.class) // 配合[Roboletric](http://robolectric.org/)运行本地测试
public class LoginViewModelTest {
    @Mock private ApiService apiService;
    private LoginViewModel viewModel;
    
    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        viewModel = new LoginViewModel(apiService);
    }
    
    @Test
    public void testLoginSuccess() {
        // 预设Mock行为
        when(apiService.login(anyString(), anyString()))
            .thenReturn(Observable.just(new LoginResponse("token123")));
            
        viewModel.login("user", "pass");
        
        // 验证交互
        verify(apiService).login("user", "pass"); // 确认API被调用
        verifyNoMoreInteractions(apiService); // 确保没有多余调用
    }
}

Stub处理复杂响应场景

模拟分页加载失败的边缘情况,使用OkHttp的MockWebServer作为网络Stub:

@Test
public void testLoadMoreFailed() {
    MockWebServer server = new MockWebServer();
    server.enqueue(new MockResponse().setResponseCode(500));
    
    // 在测试中使用Stub服务器
    ApiService api = new Retrofit.Builder()
        .baseUrl(server.url("/"))
        .build()
        .create(ApiService.class);
        
    // 验证错误处理逻辑
    assertThat(api.loadMore(1).execute().isSuccessful()).isFalse();
}

测试替身最佳实践

  1. 依赖注入优先:通过构造函数传入依赖,便于替换测试替身
  2. 单一职责:每个测试替身只模拟一种行为
  3. 避免过度指定:Mock只验证关键交互,不关注实现细节
  4. 定期重构:当Fake逻辑超过200行,考虑使用嵌入式数据库替代

完整测试代码示例可参考项目测试章节,包含更多Robotium UI测试案例。合理使用测试替身能使单元测试覆盖率提升至80%以上,同时将构建时间缩短60%。

扩展学习资源

【免费下载链接】awesome-android A curated list of awesome Android packages and resources. 【免费下载链接】awesome-android 项目地址: https://gitcode.com/gh_mirrors/awe/awesome-android

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值