OkHttp MockWebServer新实例无法接受连接问题求助
问题诊断与解决方案
看起来你遇到的问题根源在于RxJava异步操作和测试生命周期的时序不匹配,具体细节如下:
- 第一个测试类是UI测试,Espresso框架会自动等待所有UI相关的异步操作完成后才结束测试方法,所以
@After注解的tearDown会在测试逻辑真正收尾后才关闭MockWebServer,不会出现异常。 - 第二个测试类是纯服务层测试,你用
subscribeOn(Schedulers.io())发起了异步订阅,但测试方法testSuccessResponse会在订阅发出后立刻执行完毕,直接触发@After的tearDown关闭服务器。而这时候RxJava的异步请求才刚要发送,自然就会碰到Socket closed的错误。
解决方法1:用CountDownLatch阻塞测试线程,等待异步回调完成
修改testSuccessResponse方法,通过CountDownLatch让测试线程等待订阅回调执行完毕:
@Test @Throws(Exception::class) fun testSuccessResponse() { // 初始化CountDownLatch,计数为1,等待1次回调触发 val latch = CountDownLatch(1) //Given server.enqueue(MockResponse() .setResponseCode(200) .setBody("{...}") ) loginService.login("login", "password") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { // 根据实际业务逻辑调整断言 Assert.assertTrue(true) latch.countDown() // 回调完成,释放计数 }, { Assert.fail("请求失败:${it.message}") latch.countDown() // 即使失败也要释放计数,避免测试永久阻塞 } ) // 阻塞测试线程,最多等待5秒(可根据实际场景调整时长) val awaitSuccess = latch.await(5, TimeUnit.SECONDS) Assert.assertTrue("异步请求超时", awaitSuccess) }
解决方法2:改用blockingSubscribe同步执行订阅
如果不需要在主线程观察结果,可以去掉observeOn(AndroidSchedulers.mainThread()),改用blockingSubscribe让订阅同步执行,测试方法会等待操作完成后再继续执行:
@Test @Throws(Exception::class) fun testSuccessResponse() { //Given server.enqueue(MockResponse() .setResponseCode(200) .setBody("{...}") ) loginService.login("login", "password") .subscribeOn(Schedulers.io()) .blockingSubscribe( { // 根据实际业务逻辑调整断言 Assert.assertTrue(true) }, { Assert.fail("请求失败:${it.message}") } ) // 到这里订阅已经完成,tearDown不会提前执行 }
额外优化建议
两个测试类都修改了全局静态变量ApplicationConstants.APP_BASE_URL,建议在tearDown里恢复这个变量的原始值,避免测试类之间互相干扰:
private lateinit var originalBaseUrl: String @Before override fun setUp() { super.setUp() originalBaseUrl = ApplicationConstants.APP_BASE_URL // 保存原始地址 server = MockWebServer() server.start() ApplicationConstants.APP_BASE_URL = server.url("/localhost/").toString() loginService = ServiceGenerator.createService(LoginService::class.java) } @After @Throws(Exception::class) override fun tearDown() { server.shutdown() ApplicationConstants.APP_BASE_URL = originalBaseUrl // 恢复原始地址 }
内容的提问来源于stack exchange,提问作者Maurice
相关产品推荐
相关产品推荐

