如何对含Dagger注入的简单Repository做单元测试?遇空指针异常
解决UserRepository单元测试的NullPointerException问题
看起来你遇到的NPE核心原因很明确:单元测试环境下Dagger没有自动注入UserWebService实例——毕竟单元测试默认不会启动完整的依赖注入容器,导致mUserWebService一直是null,调用它的方法自然就抛出异常了。下面给你两种最常用的解决方案,按需选择:
方案1:用Mockito模拟WebService依赖(推荐纯单元测试场景)
单元测试的核心是隔离目标类的逻辑,不需要依赖真实的Web服务(哪怕它本身可用)。用Mockito模拟UserWebService是最合理的做法,步骤如下:
- 先添加Mockito依赖到你的测试gradle配置中(如果还没加):
testImplementation 'org.mockito:mockito-core:5.6.0' testImplementation 'org.mockito:mockito-inline:5.6.0' // 支持模拟final类/接口
- 在测试类中模拟依赖并手动注入到Repository:
import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import retrofit2.Call; import retrofit2.Response; import static org.mockito.Mockito.*; public class UserRepositoryTest { @Mock private UserWebService mockUserWebService; @Mock private Call<UserResponse> mockCall; private UserRepository userRepository; @Before public void setUp() { // 初始化所有Mock对象 MockitoAnnotations.openMocks(this); // 手动创建Repository并注入Mock依赖 userRepository = new UserRepository(); // 这里推荐给UserRepository加一个包级别的setter,或者用构造注入(更优雅,看下面的注意点) userRepository.setUserWebService(mockUserWebService); // 模拟WebService的getUser()返回预设的Call对象 when(mockUserWebService.getUser()).thenReturn(mockCall); // 可以进一步模拟Call的执行结果,比如返回成功响应 when(mockCall.execute()).thenReturn(Response.success(new UserResponse())); } @Test public void testGetUser_Success() throws Exception { // 调用Repository的目标方法 userRepository.getUser(); // 验证依赖方法是否被正确调用 verify(mockUserWebService, times(1)).getUser(); verify(mockCall, times(1)).execute(); } }
方案2:使用Dagger测试组件(集成测试场景)
如果你需要测试完整的依赖链,或者必须用真实的WebService实例,可以创建专门的Dagger测试组件:
- 编写测试用的Module,绑定测试环境的
UserWebService:
@Module public class TestUserModule { @Provides UserWebService provideUserWebService() { // 这里可以返回真实的WebService实例(测试环境地址),或者Mock对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://your-test-api-domain.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); return retrofit.create(UserWebService.class); } }
- 创建测试组件:
@Component(modules = TestUserModule.class) public interface TestUserComponent extends UserComponent { void inject(UserRepositoryTest test); }
- 在测试类中初始化组件并完成注入:
public class UserRepositoryTest { @Inject UserWebService userWebService; private UserRepository userRepository; @Before public void setUp() { TestUserComponent component = DaggerTestUserComponent.create(); component.inject(this); // 注入Repository的依赖 userRepository = new UserRepository(); userRepository.setUserWebService(userWebService); } // 编写你的测试方法... }
额外优化建议
给UserRepository改用构造注入,这样测试时可以直接传入依赖,不用反射或setter,代码更优雅:
public class UserRepository { private final UserWebService mUserWebService; @Inject public UserRepository(UserWebService userWebService) { mUserWebService = userWebService; } // 你的业务方法... }
测试时直接userRepository = new UserRepository(mockUserWebService)即可,非常方便。
内容的提问来源于stack exchange,提问作者Andritchi Alexei
相关产品推荐
相关产品推荐

