2010-10-16 76 views
18

我正在从Android Web应用程序中下载JSON数据。该类分析数据看起来是这样的:如何单元测试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的类?

回答

46

所有你需要做的是以下行添加到您的build.gradle依赖部分:

testCompile 'org.json:json:20140107' 

注意,也需要使用Android Studio 1.1或更高版本,并且至少需要构建工具版本22.0.0或更高才能使用!

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

+0

感谢您的Xaver!我开始认为没有人试图用最新的编译工具来使用单元测试。 – 2015-07-23 19:21:51

19

编辑:看到更新的答案在最后

我发现这个问题的答案之一。它不能解决我的问题,但至少它解释了为什么有问题。

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

这个发现提高了我的自信心。通过在我的单元测试项目中增加对android.jar的引用可以很容易地解决这个问题 - 或者至少是我想了一会儿。

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

至少这给了我更多的东西来谷歌:我的运行测试时,所以只能这样做给了我另一个错误。然而,我发现,并不是真的令人鼓舞(又一个经典的“杜'时刻)...

这个博客很好地描述了这个问题:Why Android isn’t ready for TDD, and how I tried anyway。如果你不懒得看整个事情,简要说明如下:

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.

如果再继续做牢记这一点一些谷歌上搜索,我发现了几个文章,博客也问题在这里SO有关问题。我会在这里结束添加几个环节,对于那些可能会寻找:

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

有几个建议/解决方案/替代方法在Android的单元测试在许多这些链接的,但我不会刻意去设法使基于这样一个很好的答案(因为有显然太多我仍然不了解Android开发)。如果有人有任何好的建议,虽然,我会很高兴地听到关于他们:)

UPDATE:
尝试多一点之后,我竟设法找到一个有效的解决方案,以这一特定问题。我之所以没有尝试这一点,是因为我认为我曾经在某处读过在我的Android应用程序中包含“普通”Java库的问题。我尝试了很多不同的方式来解决我的问题,所以我想我也会试试这个方法 - 它确实有效!这里是如何:我

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

没有改变我代码,没有改变我的测试,只添加这个库。一切正常,我的Android应用程序和我的单元测试。

我还测试通过在调试模式下的代码步进,和调试的Android当App在我的JsonCourseParserJSONObject从Android SDK(的android.jar)取出,但是在调试我的单元测试时,它是从取出我自己的json.jar。不知道这是否意味着在构建应用程序时不包括json.jar,或者如果运行时智能地选择要使用的正确库。也不知道这个额外的库可能对我的最终应用程序有任何其他影响。我想只有时间会告诉...

+0

感谢您对问题的详细分析和描述!而且时间是否告诉你这个问题? – paweloque 2013-01-23 22:05:45

+0

从源代码构建jar为我工作!这似乎真的很愚蠢,Android不包括在SDK中的这个地方 – lifeson106 2014-06-12 21:29:11

0

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

-1

我在我的JSON测试中遇到了这个确切的问题。您的更新回答让我尝试了一种更简单的方法,它可以用于我的项目。这使我可以运行JUnit测试,使用“真实” org.json JAR,我非Android类在Eclipse中:从这里(或地方)

  1. 下载org.json的json.jar:http://mvnrepository.com/artifact/org.json/json
  2. 文件夹“库测试”添加到您的项目,复制json.jar文件将其
  3. 在Eclipse开放:运行/运行配置,选择JUnit/YourTestClass
  4. 在Classpath选项卡,去除“ Google API“从引导条目
  5. 单击”添加JAR “,导航到/libs-test/json.jar并添加它。
  6. 单击“关闭”
  7. 运行测试
2

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

+0

'keepStartingWith“org.json。”' – 2017-10-23 14:49:37