Android UI测试后清除Firestore模拟器数据库遭遇404错误的问题
我完全理解你的场景——在Android UI测试中用Firestore模拟器做测试,想在每个测试结束后清空数据库,curl调用本地模拟器API没问题,但自己用HttpURLConnection写的代码却返回404,已经配置了网络安全规则却还是不行。下面给你几个排查方向和解决方案:
1. 检查URL中特殊字符的转义问题
你代码里直接拼接的(default)是URL保留字符,curl会自动处理这类特殊字符,但Java的URL类在构造时可能没有正确转义,导致实际请求的路径和预期不符,这是很常见的404诱因。
你可以手动转义括号部分,修改URL构造的代码:
try { // 手动转义(default)为URL安全的格式 String encodedDatabaseName = "(default)".replace("(", "%28").replace(")", "%29"); String urlStr = "http://10.0.2.2:8080/emulator/v1/projects/" + projectId + "/databases/" + encodedDatabaseName + "/documents"; url = new URL(urlStr); } catch (MalformedURLException exception) { Log.e("URLError", Objects.requireNonNull(exception.getMessage())); }
或者用URLEncoder做标准转义:
try { String databaseName = "(default)"; String encodedDatabaseName = URLEncoder.encode(databaseName, StandardCharsets.UTF_8.name()); String urlStr = "http://10.0.2.2:8080/emulator/v1/projects/" + projectId + "/databases/" + encodedDatabaseName + "/documents"; url = new URL(urlStr); } catch (MalformedURLException | UnsupportedEncodingException exception) { Log.e("URLError", Objects.requireNonNull(exception.getMessage())); }
2. 确认测试代码拥有网络权限
即使配置了network_security_config,你的Android测试代码也需要INTERNET权限才能访问Firestore模拟器的API。请检查测试目录下的AndroidManifest.xml(src/androidTest/AndroidManifest.xml)是否添加了权限声明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="你的测试包名"> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
很多时候主Manifest的权限不会自动继承到测试环境,所以需要显式声明。
3. 用OkHttp替代HttpURLConnection简化调试
HttpURLConnection的API比较繁琐,对特殊字符的处理也容易出问题,推荐用OkHttp来发送请求,它会自动处理URL编码,还能更方便地获取详细的错误响应:
首先在Module级别的build.gradle中添加测试依赖:
androidTestImplementation("com.squareup.okhttp3:okhttp:4.12.0")
然后修改tearDown方法:
@After public void tearDown() { String projectId = "test"; OkHttpClient client = new OkHttpClient(); String url = "http://10.0.2.2:8080/emulator/v1/projects/" + projectId + "/databases/(default)/documents"; Request request = new Request.Builder() .url(url) .delete() .build(); try (Response response = client.newCall(request).execute()) { Log.i("ResponseCode", "[Response Code: " + response.code() + "] msg: " + response.message()); if (!response.isSuccessful()) { // 打印错误响应体,获取模拟器返回的具体错误原因 String errorDetails = response.body() != null ? response.body().string() : "No error body"; Log.e("TeardownError", "Failed to clear database: " + errorDetails); } } catch (IOException e) { Log.e("TeardownError", "Request failed", e); } }
如果还是404,错误响应体里通常会有模拟器给出的具体提示,比如路径不存在,能帮你快速定位问题。
4. 对比curl和Android代码的请求细节
你可以启动Firestore模拟器时加上--debug参数,这样模拟器会打印所有收到的HTTP请求的完整细节(包括路径、请求头、方法等),然后对比curl发送的请求和Android代码发送的请求,就能直接看出两者的差异,比如路径是否一致、请求头是否缺失等。
举个启动模拟器的debug命令例子:
firebase emulators:start --only firestore --debug
备注:内容来源于stack exchange,提问作者akalanka

