I'm writing some POJO tests for my Android code.
I want to run them locally with the JDK (not with Dalvik on the emulator) - for speed, JUnit 4, Mockito, and being able to run headless without a device - so I have a separate "Java" project in Eclipse.
If the method I'm testing happens to reference anything from the Android SDK, e.g. android.util.Log
, the test fails - this makes sense because android.jar
isn't in the classpath. To reproduce I have this test case:
public class FooTests {
@Test
public void testFoo() {
android.util.Log.d("x", "y");
}
}
If I add android.jar
explicitly to the test project's classpath, I get exceptions like
java.lang.RuntimeException: Stub!
at android.util.Log.d(Log.java:7)
at com.example.FooTests.testFoo(FooTests.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
at org.eclipse.jdt.interna开发者_JS百科l.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Is there a way to make the code work without mocking out every last bit of Android SDK dependency? Perhaps a mocked-out android.jar
already exists?
EDIT: For now I ended up wrapping classes like android.util.Log
and injecting them into the instances, for classic IOC-based testing. Scott's PowerMock suggestion is my next step when I'll need it.
LATER EDIT: Robolectric!
I had the same problem. I wanted to test simple POJOs locally.
Specifically, my code wanted to use android.util.Base64.
What I ended up doing was to use the SDK to install the Android 4 sources, and copied the android.util.Base64 class to my project.
Surprisingly enough, this worked.
There's no mocked out android.jar that I know of. I use Powermock to mock everything out. One thing you can do to help with the heavy mocking is to make your extended android classes like activity, fragments, broadcasters, etc. thin and have them delegate to pojo classes. You could make a risk based decision not to isolation unit test the android extended classes, and instead integration unit test them via the android testing framework or something else like Robotium.
For true isolation unit testing in Android, for me unit testing on the java jvm mocking all out all collaborating classes is the best way to go.
I had the same problem. I wanted to test simple business logic locally.
Specifically, my code uses android.util.SparseArray<E>
.
I tried 3 different approaches to make it testable with junit outside android.
- In my business logic I created an interface and attached it to
MySparseArray<E>
that inherits fromSparseArray<E>
. In the junit-test I re-implemented the interface usingHashMap<Integer, E>
. This works but in the long run this is to much work to make business logic unit-testable if otherandroid.*
are required. - As suggested by @Tal Weiss I added the android java sources of the required classes:
android.util.SparseArray.java
that usescom.android.internal.util.ArrayUtils.java
. This works, too but I don't like to add more android sources, especially if there are much more dependencies - I downloaded a stub free binary
android.jar
for an old android 2.2 from http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1 and included it as lib into my junit-java-project. This jar contains only classes for namespacesandroid.*
,com.android.*
,dalvik.*
andframework.base.*
. This works too.
Currently I succeeded to avoid using android.*
classes except SparseArray<E>
in my business layer and has no dependency to Context, Activity or Service. I could have used HashMap<Integer, E>
in the android-business layer instead of SparseArray<E>
.
unmock-plugin could help https://github.com/bjoernQ/unmock-plugin. In my case it works with SparseArray.
Won't the embedded android/junit tests do what you want?
精彩评论