如何用Mockito测试Retrofit回调(MVP+Dagger2)及RecyclerView适配器变化
嘿,我来帮你搞定这个测试问题!你遇到的空指针大概率是因为测试环境里没正确初始化Presenter、Activity或者Mock网络服务,咱们一步步梳理正确的测试方案:
核心思路
要测试“成功下载Posts后RecyclerView适配器大小变化”,核心是用Mock服务模拟服务器的成功响应,让Presenter调用这个Mock服务,最后验证Activity里的RecyclerView适配器是否正确更新数据。咱们可以用Square的MockWebServer来模拟Retrofit的网络请求,配合Robolectric(用来模拟Android组件生命周期)或者Mockito(用来Mock依赖)来完成测试。
具体实现步骤
1. 先准备测试依赖
确保你的build.gradle(Module级)里添加了这些测试依赖:
testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.12.1' testImplementation 'com.squareup.okhttp3:mockwebserver:4.11.0' testImplementation 'com.google.code.gson:gson:2.10.1' testImplementation 'org.mockito:mockito-core:5.6.0'
2. 初始化测试环境(解决空指针的关键)
在测试类的@Before方法里,要把所有需要的对象都初始化好:MockWebServer、指向Mock服务器的Retrofit实例、Presenter,以及模拟/启动的Activity。
public class PostsPresenterTest { private MockWebServer mockWebServer; private PostsApi postsApi; private PostsPresenter presenter; private MainActivity activity; @Before public void setup() throws IOException { // 初始化MockWebServer mockWebServer = new MockWebServer(); mockWebServer.start(); // 创建指向Mock服务器的Retrofit实例 Retrofit retrofit = new Retrofit.Builder() .baseUrl(mockWebServer.url("/")) .addConverterFactory(GsonConverterFactory.create()) .build(); postsApi = retrofit.create(PostsApi.class); // 用Robolectric启动Activity,确保生命周期走到onCreate(初始化RecyclerView和适配器) activity = Robolectric.buildActivity(MainActivity.class) .create() .resume() .get(); // 初始化Presenter,传入Mock的API和Activity作为View(假设Activity实现了PostsView接口) presenter = new PostsPresenter(postsApi, activity); } @After public void teardown() throws IOException { mockWebServer.shutdown(); } }
3. 编写测试方法
模拟服务器返回成功的Posts列表,触发Presenter的下载逻辑,最后验证适配器的大小:
@Test public void fetchPosts_success_updatesRecyclerView() throws IOException { // 1. 准备模拟的Posts数据,转成JSON格式 List<Post> mockPosts = Arrays.asList( new Post(1, "First Post", "This is my first post"), new Post(2, "Second Post", "This is my second post") ); String mockResponseJson = new Gson().toJson(mockPosts); // 2. 让MockWebServer返回这个成功的JSON响应 mockWebServer.enqueue(new MockResponse() .setResponseCode(200) .setBody(mockResponseJson) .addHeader("Content-Type", "application/json")); // 3. 触发Presenter的下载方法 presenter.fetchPosts(); // 4. 等待后台网络请求完成(Robolectric的方法,刷新线程调度器) Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); // 5. 验证RecyclerView适配器的itemCount是否和模拟数据一致 RecyclerView recyclerView = activity.findViewById(R.id.posts_recycler_view); assertNotNull("RecyclerView适配器未初始化", recyclerView.getAdapter()); assertEquals("适配器大小不符合预期", mockPosts.size(), recyclerView.getAdapter().getItemCount()); }
空指针的常见排查点
如果还是出现空指针,你可以检查这几点:
- Activity的RecyclerView/适配器是否在onCreate里初始化:Robolectric的
buildActivity().create()会触发onCreate,确保你的Activity在这里完成了RecyclerView和适配器的初始化。 - Presenter的依赖是否注入正确:测试里的Presenter必须使用Mock的
PostsApi,不能用生产环境的实例(否则会调用真实网络,导致空指针或超时)。 - View接口方法是否正确实现:比如Presenter在下载完成后会调用
activity.showPosts(List<Post>),这个方法里必须把数据设置给RecyclerView的适配器,否则适配器还是空的。
另外,如果你不想启动真实的Activity,也可以用Mockito MockPostsView接口,先验证Presenter是否正确调用了View的方法,再单独测试Activity的适配器更新逻辑:
// 用Mockito Mock View接口 private PostsView mockView = Mockito.mock(PostsView.class); @Before public void setup() throws IOException { mockWebServer = new MockWebServer(); mockWebServer.start(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(mockWebServer.url("/")) .addConverterFactory(GsonConverterFactory.create()) .build(); postsApi = retrofit.create(PostsApi.class); presenter = new PostsPresenter(postsApi, mockView); } @Test public void fetchPosts_success_callsShowPosts() throws IOException { List<Post> mockPosts = Arrays.asList(new Post(1, "Test Post", "Test Body")); String json = new Gson().toJson(mockPosts); mockWebServer.enqueue(new MockResponse().setBody(json).setResponseCode(200)); presenter.fetchPosts(); Robolectric.flushBackgroundThreadScheduler(); // 验证Presenter调用了View的showPosts方法 Mockito.verify(mockView).showPosts(mockPosts); }
内容的提问来源于stack exchange,提问作者TomaszKopacz
相关产品推荐
相关产品推荐

