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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3951274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 07:29:01  来源:igfitidea点击:

How to unit test JSON parsing

javaandroidjsonunit-testing

提问by Julian

I'm working on an Android app in which I download JSON data from a web service. The class parsing the data looks something like this:

我正在开发一个 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;
    }
}

This builds just fine and it works when I call parseCourse()from within my app, but when I try to test this method in a unit test I get this exception:

这构建得很好,当我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.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.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

It seems that the classes of the org.jsonpackage which come with the Android Framework are not available in Java by default.

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

Is there a way I can fix this so I can unit test the classes which parse JSON?

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

采纳答案by Xaver Kapeller

All you need to do is add the following line to the dependency section in your build.gradle:

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

testImplementation 'org.json:json:20140107'

Note that you also need to use Android Studio 1.1or higher and at least build tools version 22.0.0or above for this to work!

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

testImplementationmeans that the dependency is included as a test dependency will only be used when your project is being compiled for executing unit tests. The supplied jar will not be included in your APK and will only be used to replace the missing classes in the org.jsonpackage.

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

回答by Julian

Edit: see updated answer at the end

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

I've found the answer to this one. It doesn't solve my problem, but at least it explains whythere is a problem.

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

First, I made the faulty assumption that the JSONObject(imported from the org.jsonpackage) was included as a part of the JRE. It isn't - in an Android project, this resides in android.jar(classic "duh" moment).

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

This discovery boosted my self confidence a bit. This could easily be solved by adding a reference to the android.jarin my unit test project - or at least so I thought for a brief moment. Doing so only gave me another error when running my test:

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

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

At least this gave me something more to google for. What I found however, wasn't really encouraging (yet another classic "duh" moment)...

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

This blog describes the problem pretty well: Why Android isn't ready for TDD, and how I tried anyway. If you don't bother to read the whole thing, the brief explanation is as follows:

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

The problem here is that the android.jar supplied with the SDK is stubbed out with no implementation code. The solution that seems to be expected is that you should run your unit tests on the emulator or a real phone.

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

When further doing some googling with this in mind, I found several articles, blogs and also questions here on SO regarding the issue. I'll add a few links here at the end, for those that might be looking:

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

And there are plenty more if you look around.

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

There are several suggestions/solution/alternative approaches to unit testing in Android in many of those links, but I won't bother to try to make a good answer based on that (as there is obviouslyway too much I still don't know about Android development). If anybody has any nice tips though, I'll be glad to hear about them :)

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

UPDATE:
After experimenting a bit more, I actually managed to find a working solution to this specific problem. The reason why I didn't try this in the first place, was that I thought I had read somewhere that it would be problematic to include "normal" java libraries in my Android app. I was trying so many different things to get around my problem, so I thought I'd just give this a try as well - and it actually worked! Here is how:

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

  • I downloaded the source for the "real" org.jsonpackage from here: http://www.json.org/java/index.html
  • Next I compiled the source code and packed it together in my own json.jar
  • I added the newly created json.jarto the build path of my project (the main project of my Android application, not the test project)
  • org.json从这里下载了“真实”包的源代码:http: //www.json.org/java/index.html
  • 接下来我编译源代码并打包在我自己的json.jar中
  • 我将新创建的json.jar添加到我的项目的构建路径中(我的 Android 应用程序的主项目,而不是测试项目)

No changes to my code, no changes to my test, only adding this library. And everything works, both my Android app and my unit tests.

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

I also tested stepping through the code in debug mode, and when debugging the Android app the JSONObjectin my JsonCourseParseris fetched from the Android SDK (android.jar), but when debugging my unit test it is fetched from my own json.jar. Not sure if this means that the json.jarisn't included when my app is built, or if the runtime intelligently selects the correct library to use. Also not sure if this extra library might have any other effects on my final app. I guess only time will tell...

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

回答by Bevor

I had the same problem but I found a better way. The framework Roboelectricdoes the job. At first I just added the roboelectric.jar as external library (because I don't use Maven) to the build path of my Java JUnit test project and as "class annotation" I added @RunWith(RobolectricTestRunner.class). But then I got a NoClassDefFoundError: android/net/Uri. After I still added the android.jar itself used by the corresponding Android project (although the Android library with its android.jar was already a part of my build path), it works.

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

回答by KevH

I had this exact problem in my JSON tests. Your updated answer led me to try a simpler approach, which worked for my project. It allows me to run JUnit tests, using the 'real' org.json JAR, on my non-Android classes from within Eclipse:

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

  1. Download org.json's json.jar from here (or wherever): http://mvnrepository.com/artifact/org.json/json
  2. Add a folder 'libs-test' to your project, copy the json.jar file to it
  3. In Eclipse open: Run / Run Configurations, select JUnit / YourTestClass
  4. On the Classpath tab, remove "Google APIs" from Bootstrap entries
  5. Click "Add JARs", navigate to /libs-test/json.jar and add it.
  6. Click "Close"
  7. Run your tests
  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. 运行你的测试

回答by Erik Browne

I'm using Bj?rn Quentin's UnmockGradle plugin, which allows you to replace stubbed classes in the default android.jar with real versions from another android.jar (e.g. Robolectric).

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