Simple non-API Android JUnit test in Eclipse with

2019-01-26 18:29发布

问题:

I run non-Android JUnit tests from within Eclipse every day. Today I wanted to test some of my Android library classes. Oh, the pain.

I have an Android library project using android-maven-plugin. I have source files in src/main/java and my (new) unit test in src/test/java. My POM has the appropriate JUnit dependencies and android-maven-plugin references.

Sometimes I create an Android Uri instance from a File. Sometimes I have an existing Java URI instance that I've created from a File which I then convert to a Uri. Since I trust neither Java nor Android with files and URIs (don't get me started on how Java mangles UNC paths in URIs, or how Java breaks the equals() contract in URIs), I wanted to create a simple unit test to create a temp file, create Uris from two different approaches, and make sure they come out equal.

So I make a little JUnit unit test like I'm used to, and try to run it in Eclipse using Ctrl+F11. Eclipse asks me if this is an "Android JUnit Test" or a "JUnit Test". Well, Android, obviously. So I choose the first option and get:

[2013-03-23 21:37:10 - mylib] ------------------------------
[2013-03-23 21:37:10 - mylib] Android Launch!
[2013-03-23 21:37:10 - mylib] adb is running normally.
[2013-03-23 21:37:10 - mylib] Could not find mylib.apk!

Hmmm... that wasn't very successful. So I delete the run configuration and try just "JUnit Test". Now I get a different dialog, asking me to select my preferred launcher, either "Android JUnit Test Launcher" or "Eclipse JUnit Test Launcher". It doesn't matter which I choose; I get:

Class not found com.example.MyUnitTest
java.lang.ClassNotFoundException: com.example.MyUnitTest
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:693)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:429)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
    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)

I've read that with the android-maven-plugin I can run unit tests locally in Eclipse if they just use classes in the Android jar but don't make any API calls, which is what I'm doing here. So how do I pull that off?

回答1:

A workaround is to click the "Run As..." button in the toolbar and then select "Run Configurations...". If you select the JUnit launcher you created and go to its "Classpath" tab you can add the bin/classes folder to the JUnit launcher classpath. This should now run.



回答2:

Do not use Run As - Android JUnit Test as it is for running Android Test Project only.

When use Run As - JUnit Test, the ClassNotFoundException is due to inconsistence between ADT and Eclipse built-in JUnit Test Runner regards to the project output folder. ADT generates all .class files under bin/classes, whereas built-in JUnit Test Runner looking for .class files under target/classes. Your Android project in Eclipse never use target/classes so it remains empty, this is the reason why you get ClassNotFoundException exception.

AFAIK there is no way to alter Eclipse built-in JUnit Test Runner to use a different folder than the default target/classes. Check out Ricardo's answer to see how to add bin/classes to built-in JUnit Test Runner's classpath. Also note that you cannot alter your Android project's default output folder to something else than ../bin/classes either, as it will break ADT build process.

The dirty workaround (for solving ClassNotFoundException) is manual copy everything under bin\classes to target\classes, note that you need do this every time you change the source code.

This is not a problem when running mvn test from command line or via Eclipse, as Maven use target\classes and know how to fill it properly. note that by using this approach, you will not able to use the JUnit window with nice red/green error bar inside Eclipse.



回答3:

An android junit test should be located in a separate module as setup in the Android Maven Plugin samples projects (e.g. the morseflash example). This is due to the overloaded path setting and the need to build the apk and deploy it on the device/emulator to run the test. Android junit tests are NOT unit tests at all, but rather integration test (or in this case called instrumentation tests).