繁体   English   中英

如何对 JSON 解析进行单元测试

[英]How to unit test JSON parsing

我正在开发一个 Android 应用程序,我在其中从 Web 服务下载 JSON 数据。 解析数据的类看起来像这样:

public class JsonCourseParser implements CourseParser {

    public Course parseCourse(String courseData) {
        Course result;

        try {
            JSONObject jsonObject = new JSONObject(courseData);

            ...

            result = new Course("foo", "bar");
        } catch (JSONException e) {
            result = null;
        }

        return result;
    }
}

这构建得很好,当我从我的应用程序中调用parseCourse()时它可以工作,但是当我尝试在单元测试中测试这个方法时,我得到了这个异常:

java.lang.NoClassDefFoundError: org/json/JSONException
    at JsonCourseParserTest.initClass(JsonCourseParserTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassNotFoundException: org.json.JSONException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
    ... 16 more

似乎默认情况下,Android 框架附带的org.json包的类在 Java 中不可用。

有没有办法解决这个问题,以便我可以对解析 JSON 的类进行单元测试?

您需要做的就是将以下行添加到 build.gradle 的依赖项部分:

testImplementation 'org.json:json:20180813'

请注意,您还需要使用 Android Studio 1.1或更高版本以及至少22.0.0或更高版本的构建工具才能使其正常工作!

testImplementation意味着该依赖项被包含为测试依赖项,只有在您的项目被编译以执行单元测试时才会使用。 提供的 jar 不会包含在您的 APK 中,只会用于替换org.json包中缺少的类。

编辑:请参阅最后的更新答案

我已经找到了这个问题的答案。 它没有解决我的问题,但至少它解释了为什么会出现问题。

首先,我错误地假设JSONObject (从org.json包导入)作为 JRE 的一部分包含在内。 它不是 - 在 Android 项目中,它位于android.jar (经典的“duh”时刻)。

这个发现让我有点自信。 这可以通过在我的单元测试项目中添加对android.jar的引用来轻松解决 - 或者至少我想了一会儿。 这样做只会在运行测试时给我另一个错误:

java.lang.RuntimeException: Stub!
    at org.json.JSONObject.<init>(JSONObject.java:8)
    ...

至少这给了我更多的东西去谷歌搜索。 然而,我发现的并不是真正令人鼓舞的(又一个经典的“废话”时刻)......

这篇博客很好地描述了这个问题: 为什么 Android 还没有为 TDD 做好准备,以及我是如何尝试的 如果你懒得看全文,简要说明如下:

这里的问题是 SDK 提供的 android.jar 没有实现代码。 似乎可以预期的解决方案是您应该在模拟器或真实手机上运行单元测试。

考虑到这一点,在进一步使用谷歌搜索时,我发现了几篇关于此问题的文章、博客和问题。 最后,我将在此处添加一些链接,供可能正在寻找的人使用:

如果你环顾四周,还有更多。

在许多这些链接中,有几个建议/解决方案/替代方法在 Android 中进行单元测试,但我不会费心尝试基于此做出好的答案(因为显然有太多我仍然不知道的方法)关于Android开发)。 如果有人有任何好的提示,我会很高兴听到他们的消息:)

更新:
在进行了更多试验后,我实际上设法找到了解决此特定问题的可行解决方案。 我之所以没有首先尝试这个,是因为我认为我在某个地方读到过,在我的 Android 应用程序中包含“普通”Java 库是有问题的。 我尝试了很多不同的方法来解决我的问题,所以我想我也应该尝试一下 - 它确实有效! 方法如下:

  • 我从这里下载了“真正的” org.json包的源代码: http : //www.json.org/java/index.html
  • 接下来我编译源代码并打包在我自己的json.jar中
  • 我将新创建的json.jar添加到我的项目的构建路径中(我的 Android 应用程序的主项目,而不是测试项目)

我的代码没有变化,我的测试没有变化,只添加了这个库。 一切正常,无论是我的 Android 应用程序还是我的单元测试。

我还测试了在调试模式下单步执行代码,在调试 Android 应用程序时,我的JsonCourseParserJSONObject是从 Android SDK ( android.jar ) 中获取的,但是在调试我的单元测试时,它是从我自己的json.jar 中获取的 不确定这是否意味着构建我的应用程序时不包含json.jar ,或者运行时是否智能地选择了要使用的正确库。 也不确定这个额外的库是否对我的最终应用程序有任何其他影响。 我想只有时间会证明......

我正在使用 Björn Quentin 的Unmock Gradle 插件,它允许您用另一个 android.jar(例如 Robolectric)的真实版本替换默认 android.jar 中的存根类。

我遇到了同样的问题,但我找到了更好的方法。 框架Roboelectric 可以完成这项工作。 起初,我只是将 roboelectric.jar 作为外部库(因为我不使用 Maven)添加到我的 Java JUnit 测试项目的构建路径中,并作为“类注释”添加了@RunWith(RobolectricTestRunner.class) 但后来我得到了一个NoClassDefFoundError: android/net/Uri 在我仍然添加了相应的 Android 项目使用的 android.jar 本身之后(尽管带有 android.jar 的 Android 库已经是我构建路径的一部分),它可以工作。

我在 JSON 测试中遇到了这个确切的问题。 您更新后的答案使我尝试了一种更简单的方法,该方法适用于我的项目。 它允许我使用“真实的”org.json JAR 在 Eclipse 中的非 Android 类上运行 JUnit 测试:

  1. 从这里(或任何地方)下载 org.json 的 json.jar: http ://mvnrepository.com/artifact/org.json/json
  2. 将文件夹“libs-test”添加到您的项目中,将 json.jar 文件复制到其中
  3. 在Eclipse中打开:Run/Run Configurations,选择JUnit/ YourTestClass
  4. 在 Classpath 选项卡上,从 Bootstrap 条目中删除“Google APIs”
  5. 单击“添加 JAR”,导航到 /libs-test/json.jar 并添加它。
  6. 点击“关闭”
  7. 运行你的测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM