Like many others I was excited to hear that Mockito now works with Android and followed this tutorial to see it with my own eyes. Everything seemed fan-flapping-tastic and I got underway incorporating the mocking solution into my Android Test Project...
The error
However, on setting up my application's test project to leverage the mockito-all-1.9.5, dexmaker-1.0 and dexmaker-mockito-1.0 jars I encountered a problem with my very first test case. Precisely this problem in fact. The part that I would like assistance on is;
Caused by: java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
I have been informed that this "simply doesn't quite work yet" since the stack trace implies that the DexMaker jar is not being used - reference this response. However, I am suspicious that I am doing something wrong with respect to my project set-up so I'm looking to draw from the collective knowledge base here to see if indeed this is user error or a beta-bug.
My Android Test Project set-up
Please find below a screenshot of my test project's configuration. The project was created via the Android Wizard and shares no special features other than the inclusion of the Mockito and DexMaker jars (mentioned above) under the libs directory.
The Test
Never mind the content of the test (the test fails before the unit test is executed) the set-up is as described below;
public class TestSpotRatingCalculator extends InstrumentationTestCase {
#Mock
private AService aService; // Changed the service names being used here - not important.
#Mock
private BService bService;
#Mock
private CService cService;
#Mock
private DService dService;
/**
* #see android.test.AndroidTestCase#setUp()
*/
#Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this); // Failure here with aforementioned stacktrace...
}
If anyone out there has an idea what is wrong then please sound-off here.
Hi I had the same problem and I found this article really usefull!
http://corner.squareup.com/2012/10/mockito-android.html
The key piece of information is:
To use Mockito on a device or emulator, you’ll need to add three .jar
files to your test project’s libs directory: mockito-all-1.9.5.jar,
dexmaker-1.0.jar, and dexmaker-mockito-1.0.jar.
Just add this in your gradle:
androidTestCompile 'org.mockito:mockito-core:1.10.8'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
We just had the same problem in a project, but our tests also failed on a real device.
The cause was tracked to how Mockito uses the class loader, and resulted in the following error in LogCat:
W/ActivityThread(5777): ClassLoader.getResources: The class loader returned by Thread.getContextClassLoader() may fail for processes that host multiple applications. You should explicitly specify a context class loader. For example: Thread.setContextClassLoader(getClass().getClassLoader());
The fix was to explicitly set the class loader before calling mock() a test, eg.
#Override
protected void setUp() throws Exception {
super.setUp();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
fooImpl = mock(Foo.class)
}
The problematic file in Mockito is this one: org.mockito.internal.configuration.ClassPathLoader (line 121 in 1.9.5)
As hinted at here the dexmaker-android combo only works 100% when the instrumented tests are run against a real device.
Running the tests against a real device do not exhibit this failure.
For everybody who still have this error, check if you didn't exclude a class in the dependecies. We exluded by accident the MockMaker.class so this was then the cause for the exception.
Related
I have model
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ProductsRequest {
private String initiatorType;
private String categoryCode;
I have lombok config:
lombok.anyConstructor.suppressConstructorProperties = true
lombok.addGeneratedAnnotation = false
On android with API 27(Android 7 on real device) all work fine. On android 17(Android 4.2 on emulator) In this line I get error:
return restApiFactory.getProductService().getProducts(productsRequest);
error:
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.beans.ConstructorProperties" on path: /data/app/my-1.apk
If I change
#AllArgsConstructor
#NoArgsConstructor
to standart constructors - all work fine
Because I never experienced such issue with ctor-s I encourage you to describe your problem in more detail. I assume the code you manually wrote somehow differs from the code lombok generated. Might be the visibility of methods or some special annotation added.
Using delombok feature (https://projectlombok.org/features/delombok) you will expand the annotations to real code. Than you can diff your manually written code and lombok generated code. So you can explore if #java.beans.ConstructorProperties added or not above the code lombok generated. (Delombok using Gradle)
Note: Actually you will have 3 ctor: #AllArgsConstructor, #NoArgsConstructor and #RequiredArgsConstructor is implicitly covered in #Data.
Based on your lombok.config file, ctor-s should not have an annotation. Could it be that some of your flavors are missing the lombok.config on class path?
I'm trying to come to terms with the new unit test feature of Android Studio.
I've followed the instructions on http://tools.android.com/tech-docs/unit-testing-support. The description there explicitly mentions the 'Method ... not mocked' error and suggests to put the following into the build.gradle:
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
This works in so far as the tests run when started from the command line with
gradlew test --continue
but not when I run the test class from Android Studio with rightclick -> run. This way, I get the same error again:
java.lang.RuntimeException: Method setUp in android.test.AndroidTestCase not mocked. See https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support for details.
at android.test.AndroidTestCase.setUp(AndroidTestCase.java)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Any ideas on how to solve this?
EDIT: The content of the test class doesn't really matter because the setUp of the test fails, I tried with the most simple class:
public class ContactFormToolTest extends AndroidTestCase {
public void testSOmething(){
assertEquals(false, true);
}
}
Also tried overriding setUp, makes no difference.
From: https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support#TOC-Method-...-not-mocked.-
The android.jar file that is used to run unit tests does not contain
any actual code - that is provided by the Android system image on real
devices. Instead, all methods throw exceptions (by default). This is
to make sure your unit tests only test your code and do not depend on
any particular behaviour of the Android platform (that you have not
explicitly mocked e.g. using Mockito). If that proves problematic, you
can add the snippet below to your build.gradle to change this
behavior:
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
The new Unit Tests feature in Android Studio fakes the entire Android SDK so that you can run fast, Java-only tests, without needing to install your application on an Android device (this is similar to Robolectric). The general idea is that you mock all the responses from the Android SDK calls.
AndroidTestCase is used to run a test with the real Android SDK.
So, your issue is that you are trying to run an AndroidTestCase that depends on the Android SDK, but your test runner is launching the Unit Tests environment, which uses a fake Android SDK instead of a real one.
You need to choose one approach. If you want a pure unit test, then you probably should use a JUnit 4 test class instead of an AndroidTestCase. More instructions here:
https://developer.android.com/training/testing/unit-testing/local-unit-tests.html#build
As of SDK version 24, AndroidTestCase is deprecated
This class was deprecated in API level 24.
Use InstrumentationRegistry instead. New tests should be written using
the Android Testing Support Library.
You are supposed to use the Espresso framework for UI testing. There is a tutorial.
I've written a unit test that simply extends TestCase and I have the following:
public class MetricParserTests extends TestCase {
#Override
protected void setUp() throws Exception {
super.setUp();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testFailure() {
fail("This needs to fail");
}
}
When I run my tests using ant test or adb shell am instrument I get the following results:
... [exec] OK (1 tests) ...
I'd expect to see a failure on the command line.
I believe I know what the issue is. I was able to reproduce the issue and solve it. The command you use does not rebuild and re-install your test project onto a device. When you call ant test it will just execute the tests which are already installed on that device.
What you need to call is the three commands in your test project's directory:
ant debug
ant installd
ant test
Then all tests will be rebuild and re-installed and latest tests will be executed. If you don't call debug and installd, the changes you did to the tests do not get applied.
I haven't had recent experience in Android testing, but here is what I have found...
You can use normal JUnit tests if your code is totally decoupled from Android (see here
for an example). This would run on your JVM using the JUnit runner.
However, if you are trying to run these tests on an Android device (either via ant, or the command line tools) then you need to create a full android test project (See here).
To test "on device" your test cases need to extend one of the Android test classes like ActivityInstrumentationTestCase2<T>
and are run using the InstrumentationTestRunner in the Dalvik VM on the Android device.
Using an IDE or the command-line tools to create a test project should create a sample test for you to work from.
This blog post linked from the comments of the post above is a good source of information, as is the Android Testing Fundamentals doc.
The method testFailure() does not have a #Test annotation. Is that correct?
I'm trying to test a method in a jar library, and was hoping to use Robolectric to do my unit testing, rather than running the tests in the Android emulator. I'm running into a problem though, where Robolectric needs an androidmanifest.xml file that doesn't exist, since I'm building a library...
Is there any way to run Robolectric tests without an app?
Here's what my test case and code under test look like:
public class ObjectUnderTest {
methodUnderTest(View v) {
...
}
}
#RunWith(RobolectricTestRunner.class)
public class Tests {
#Test
public void methodUnderTest_Test() {
...
}
}
When I run the test suite I get a FileNotFoundException from Robolectric looking for androidmanifest.xml. I've tried using the JUnit4 test runner instead, but then I get the "Stub!" exception when I create a View for the argument to methodUnderTest().
Is there a way to do this besides creating a stub application just for the unit tests? Thanks!
It depends which Robolectric you're using.
If you use 2.0 you could try to annotate your test class with #Config(manifest=Config.NONE).
If you use 1.x I think it's doable but will require more effort:
You should folder create structure similar to Android project and create dummy AndroidManiifest.xml inside
You should extend RobolectricTestRunner and pass through constructor path to this fake Android project
I'm trying to test HTTP calls using the Robolectric test framework's HTTP layer.
My test class is already annotated with RunWith(RobolectricTestRunner.class). Here's a sample HTTP test case:
#Test
public void testHttpGet() {
Robolectric.addPendingHttpResponse(200, "All your requests are belong to Robolectric");
String response = ConnectionUtils.sendRequest();
assertThat(response, equalTo("All your requests are belong to Robolectric"));
}
This test fails and the JUnit Failure Trace reports the following DefaultRequestDirector exception (originating from the sendRequest() call):
java.lang.NoSuchMethodError: org.apache.http.impl.client.DefaultRequestDirector.<init>(Lorg/apache/commons/logging/Log;Lorg/apache/http/protocol/HttpRequestExecutor;Lorg/apache/http/conn/ClientConnectionManager;Lorg/apache/http/ConnectionReuseStrategy;Lorg/apache/http/conn/ConnectionKeepAliveStrategy;Lorg/apache/http/conn/routing/HttpRoutePlanner;Lorg/apache/http/protocol/HttpProcessor;Lorg/apache/http/client/HttpRequestRetryHandler;Lorg/apache/http/client/RedirectHandler;Lorg/apache/http/client/AuthenticationHandler;Lorg/apache/http/client/AuthenticationHandler;Lorg/apache/http/client/UserTokenHandler;Lorg/apache/http/params/HttpParams;)V
at org.apache.http.impl.client.AbstractHttpClient.createClientRequestDirector(AbstractHttpClient.java:660)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:625)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
Any ideas what might be causing the Apache class to throw an exception?
Using the current latest library, robolectric-1.2-20120730.165026-113-jar-with-dependencies.jar.
Found the answer on the Robolectric Google Group: https://groups.google.com/forum/#!msg/robolectric/jWwWjQ23wHI/X9b_ZdDMJg0J
Right click the Robolectric Java test project > Properties > Java Build Path > Order and Export.
Move the robolectric jar to the top of the list of libraries.
Find and remove cached-robolectric-classes.jar. If you followed Robolectric's installation instructions for Eclipse, this file will be under [your main Android project]/tmp.
Rerun JUnit and get back to work.