Android中OkHttp3 Post请求单元测试失败,求正确编写方案
嘿,你的测试失败问题其实很好解决——核心原因是OkHttp的enqueue()是异步执行的:你的assertEquals(responsePost, "200")会在onResponse()回调执行前就被调用,这时候responsePost还是初始的空字符串,自然和预期的"200"不匹配啦。
下面给你三种正确的测试方案,从简单到专业,你可以根据需求选择:
方案一:改用同步请求(快速解决简单场景)
把异步的enqueue()换成同步的execute(),这样代码会阻塞直到请求完成,就能拿到响应码再做断言:
@Test public void postData_SyncTest() throws Exception { MediaType JSON = MediaType.parse("application/json; charset=utf-8"); Map<String, String> params = new HashMap<>(); params.put("email", "a@a.a"); params.put("password", "123456"); JSONObject parameter = new JSONObject(params); OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(JSON, parameter.toString()); Request request = new Request.Builder() .url("你的真实测试接口地址") .post(body) .addHeader("content-type", "application/json; charset=utf-8") .build(); // 同步执行,直到拿到响应 Response response = client.newCall(request).execute(); String responseCode = String.valueOf(response.code()); assertEquals("200", responseCode); }
方案二:用CountDownLatch等待异步回调(保留异步逻辑)
如果必须保留异步调用的逻辑,可以用CountDownLatch让测试线程等回调执行完再断言:
@Test public void postData_AsyncWithLatchTest() throws Exception { MediaType JSON = MediaType.parse("application/json; charset=utf-8"); Map<String, String> params = new HashMap<>(); params.put("email", "a@a.a"); params.put("password", "123456"); JSONObject parameter = new JSONObject(params); OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(JSON, parameter.toString()); Request request = new Request.Builder() .url("你的真实测试接口地址") .post(body) .addHeader("content-type", "application/json; charset=utf-8") .build(); final String[] responseCode = new String[1]; // 用数组捕获回调里的值 CountDownLatch latch = new CountDownLatch(1); // 计数器初始为1 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { latch.countDown(); // 失败也要释放计数器,避免测试卡死 } @Override public void onResponse(Call call, Response response) throws IOException { responseCode[0] = String.valueOf(response.code()); latch.countDown(); // 回调完成,计数器减1 } }); // 最多等5秒,超时就结束测试 latch.await(5, TimeUnit.SECONDS); assertEquals("200", responseCode[0]); }
方案三:用MockWebServer模拟服务器(单元测试最佳实践)
直接调用真实接口做单元测试太不靠谱了——网络波动、后端环境变化都能导致测试失败。更专业的做法是用OkHttp自带的MockWebServer模拟服务器响应:
首先添加依赖(Gradle为例):
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
然后编写测试代码:
import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; public class LoginServiceTest { private MockWebServer mockServer; private LoginService loginService; @Before public void setUp() throws Exception { mockServer = new MockWebServer(); mockServer.start(); // 启动模拟服务器 // 如果需要Context,可以用MockContext或者Robolectric提供的上下文 loginService = new LoginService(null); } @After public void tearDown() throws Exception { mockServer.shutdown(); // 测试完关闭服务器 } @Test public void postData_WithMockServerTest() throws Exception { // 模拟服务器返回200响应,带上Authorization头 MockResponse mockResponse = new MockResponse() .setResponseCode(200) .addHeader("Authorization", "test-token-123"); mockServer.enqueue(mockResponse); // 获取模拟服务器的基础URL String baseUrl = mockServer.url("/").toString(); CountDownLatch latch = new CountDownLatch(1); // 调用LoginService的postData方法 loginService.postData(baseUrl, "a@a.a", "123456"); latch.await(5, TimeUnit.SECONDS); // 断言登录状态是否正确 assertEquals("logged In", loginService.logInResponse); } }
额外小建议
- 尽量不要在单元测试里依赖真实网络和后端,Mock才是稳定的选择
- 你的
LoginService直接操作Context、启动Activity、读写SharedPreferences,这些逻辑可以抽成接口解耦,后续测试会更方便 - 回调里的
((Activity)context).runOnUiThread()在测试环境容易出问题,建议把UI提示逻辑单独封装,方便Mock
内容的提问来源于stack exchange,提问作者Asif047
相关产品推荐
相关产品推荐

