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

2022-09-01 21:38:18

我正在开发一个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 Framework附带的软件包类似乎在Java中不可用。org.json

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


答案 1

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

testImplementation 'org.json:json:20180813'

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

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


答案 2

编辑:请参阅末尾的更新答案

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

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

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

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

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

这篇博客很好地描述了这个问题:为什么Android还没有为TDD做好准备,以及我是如何尝试的。如果您懒得阅读整个内容,简要说明如下:

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

在进一步进行一些谷歌搜索时,我发现了几篇文章,博客以及有关SO的问题。我将在最后添加一些链接,供那些可能正在查找的链接使用:

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

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

更新:
在进行了更多的实验之后,我实际上设法找到了解决此特定问题的工作解决方案。我一开始没有尝试这样做的原因是,我认为我已经在某个地方读到过,在我的Android应用程序中包含“正常”的java库是有问题的。我尝试了很多不同的事情来解决我的问题,所以我想我也应该尝试一下 - 它实际上有效!操作方法如下:

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

没有对我的代码进行任何更改,对我的测试没有更改,只是添加了此库。一切正常,包括我的Android应用程序和我的单元测试。

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