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

Jul*_*ian 22 java android json unit-testing

我正在开发一个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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这构建得很好,当我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
Run Code Online (Sandbox Code Playgroud)

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

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

Xav*_*ler 49

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

testImplementation 'org.json:json:20140107'
Run Code Online (Sandbox Code Playgroud)

请注意,您还需要使用Android Studio 1.1或更高版本以及至少构建工具版本22.0.0或更高版本才能实现此功能!

testImplementation表示包含依赖项,因为只有在编译项目以执行单元测试时才会使用测试依赖项.提供的jar将不会包含在您的APK中,并且仅用于替换org.json包中缺少的类.


Jul*_*ian 19

编辑:最后查看更新的答案

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

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

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

java.lang.RuntimeException: Stub!
    at org.json.JSONObject.<init>(JSONObject.java:8)
    ...
Run Code Online (Sandbox Code Playgroud)

至少这给了我更多谷歌的东西.然而,我发现的并不是真正令人鼓舞(又是另一个经典的"呃"时刻)......

这个博客很好地描述了这个问题:为什么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应用程序时JSONObject,我JsonCourseParser从Android SDK(android.jar)获取,但是在调试我的单元测试时,它是从我自己的json.jar获取的.不确定这是否意味着在构建我的应用程序时未包含json.jar,或者运行时是否智能地选择了要使用的正确库.还不确定这个额外的库是否可能对我的最终应用程序产生任何其他影响.我想只有时间会告诉......