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
Related
I am in the process of writing Android instrumented tests in an area were Robolectric custom shadows fall short.
Ideally, we want to write code that is flexible across all versions of the Android SDK however I am in an edge case situation where I need to write a individual unit test for a method that works only Marshmallow.
I also need to write a unit test that only works for Lollipop and under because of differences in operating system dependencies (i.e. java.lang.NoClassDefFoundError's)
Is there anyway I can do this through Junit4-like annotations or something similar to what Robolectric does where it can ignore running the tests if the SDK is not a good fit?
I do not want to be writing code like this:
// MarshmallowWidgetTest.java
#Test
public void testPrintName()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
// ...asserts...
}
}
// LollipopWidgetTest.java
#Test
public void testPrintName()
{
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
{
// ...asserts...
}
}
I'm not very familiar with unit testing or Robolectric, but because at time of writing unit tests by me there was no support for API 23 I used that config:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21) //this guy
public class MainActivityTest {
MainActivity_ activity = Robolectric.setupActivity(MainActivity_.class);
}
So like you see there's a annotation which you can use to your test classes.
EDIT:
Sorry that I focused only on Robolectric test framework, not main problem.
For annotating instrumentation tests for specific API I would use:
1. Class with #Before annotation
Create a class with #Before annotation, where it would check the API of tested devices. If wrong, the tests would fail in this method. Use fail(); method.
2. Use #SdkSuppress annotation
Indicates that a specific test or class requires a minimum API Level to execute.
Test(s) will be skipped when executed on android platforms less than specified level.
From: http://developer.android.com/reference/android/support/test/filters/SdkSuppress.html
So if you would set #SdkSuppress(minSdkVersion=23) it would run only on Android Marshmallow devices and if ##SdkSuppress(minSdkVersion=20) it would run only on higher 5.0 API devices.
Read also: http://www.vogella.com/tutorials/AndroidTesting/article.html
3. Create your own annotation like #SdkOnly
Maybe this article would be useful: http://help.testdroid.com/customer/portal/articles/1256803-using-annotations-in-android-instrumentation-tests
4. Create suites for your specific instrumentation tests
For this purpose you would use #RunWith() and Suites.SuiteClasses() annotations.
To organize the execution of your instrumented unit tests, you can
group a collection of test classes in a test suite class and run these
tests together. Test suites can be nested; your test suite can group
other test suites and run all their component test classes together.
A test suite is contained in a test package, similar to the main
application package. By convention, the test suite package name
usually ends with the .suite suffix (for example,
com.example.android.testing.mysample.suite).
To create a test suite for your unit tests, import the JUnit RunWith
and Suite classes. In your test suite, add the #RunWith(Suite.class)
and the #Suite.SuitClasses() annotations. In the #Suite.SuiteClasses()
annotation, list the individual test classes or test suites as
arguments.
The following example shows how you might implement a test suite
called UnitTestSuite that groups and runs the
CalculatorInstrumentationTest and CalculatorAddParameterizedTest test
classes together.
import com.example.android.testing.mysample.CalculatorAddParameterizedTest;
import com.example.android.testing.mysample.CalculatorInstrumentationTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
// Runs all unit tests.
#RunWith(Suite.class)
#Suite.SuiteClasses({CalculatorInstrumentationTest.class,
CalculatorAddParameterizedTest.class})
public class UnitTestSuite {}
From: http://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html
5. Helpful resources
http://www.netmite.com/android/mydroid/development/pdk/docs/instrumentation_testing.html
https://github.com/googlesamples/android-testing-templates/blob/master/AndroidTestingBlueprint
Hope it help
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.
Do anyone know how to integrate robolectric into android studio?
How to write sample test?
How to launch it?
I am working with android studio not to long, and I am too bad with gradle.
Searching the net didn't give me a result - I even could not launch official demo - https://github.com/robolectric/robolectric-samples . My android studio do not saw the test class.
Please give me simpliest step by step gide, thanks
Since robolectric runs in a JVM (i.e. not on a device or emulator), it is just a library and adding the test runner is all that's needed.
Make sure that the android SDK is later in the classpath than robolectric or junit - otherwise you'll get the stubbed methods from the android SDK.
#RunWith(RobolectricTestRunner.class)
public class MyActivityTest {
#Test
public void shouldHaveApplicationName() throws Exception {
String appName = new MyActivity().getResources().getString(R.string.app_name);
assertThat(appName, equalTo("MyActivity"));
}
}
See http://robolectric.org/quick-start/
When I create parameterized test cases in JUnit 3.x, I usually create a TestSuite with something like
public static Test suite() {
TestSuite s = new TestSuite();
for (int i = MIN; i < MAX; ++i) {
s.addTest(new MyTest(i));
}
}
This suite() method is called correctly when running JUnit from a desktop command-line. When I tried this with my Android test project, the tests don't run. How do I get my tests to run on the emulator? Or is there a different way to create parameterized tests for Android?
More thoughts:
Typically I run my tests with the command line:
adb shell am instrument -w [-e class <fully qualified test class name>[#<test method name>()]] <Android package name>/android.test.InstrumentationTestRunner
This allows me to select which tests to run from my test suite. Ideally, I want to run the the parameterized tests in this way as well. The link in the comment from #Appu describes building a separate app that runs JUnit tests. As part of that, this app has a custom TestRunner. I can very likely borrow these ideas to create a TestRunner which I can use in place of android.test.InstrumentationTestRunner. This seems like a lot of work for a not uncommon task. I prefer not to reinvent the wheel if there is already a similar solution in the Android API. Does anyone know of such a thing? Also, other alternative solutions will be helpful.
Nevermind, it looks like #dtmilano already posted this as an answer...
You can implement a test runner to be able to pass parameters to Android tests.
See the example at how to pass an argument to a android junit test (Parameterized tests).
Or is there a different way to create parameterized tests for Android?
We (Square) wrote a library called Burst for this purpose. If you add enum parameters in your test constructor, Burst's test runner will generate a test for each combination of enum values. For example:
public class ParameterizedTest extends TestCase {
enum Drink { COKE, PEPSI, RC_COLA }
private final Drink drink;
// Nullary constructor required by Android test framework
public ConstructorTest() {
this(null);
}
public ConstructorTest(Drink drink) {
this.drink = drink;
}
public void testSomething() {
assertNotNull(drink);
}
}
Quite a while after originally writing this question, I discovered that I can directly run a test class which contains a static suite() method:
adb shell am instrument -w -e class <fully qualified test class name> <Android package name>/android.test.InstrumentationTestRunner
However, the test suite doesn't run when I try to run all the tests in a given package.
Of course, this has been a while. Now I am using Android Studio instead of the command-line. I can still run the test class individually, but it still doesn't run when I select a package or try to run all of my tests.
A potential alternative is to write a master test class with a suite() method which adds all the tests to the returned TestCase. Unfortunately, this requires some manually editing every time I add a new test class to my suite.
I'm trying to implement a unit test using Robolectric to replace the stubbed methods in android.jar while also using jMockit to mock an Android class (Fragment, in my case). However, I can't seem to get it to work. If I annotate the test class with #RunWith(RobolectricTestRunner.class), I get:
java.lang.IllegalStateException: JMockit wasn't properly initialized; check that jmockit.jar precedes junit.jar in the classpath (if using JUnit; if not, check the documentation)
If I use #RunWith(JMockit.class) or no #RunWith, I get "Stub!" exceptions.
At the moment, my classpath has things in the following order: robolectric, jmockit, junit, android.
Anybody out there been able to get jmockit and robolectric to play well together?
This should be possible. I haven't tested this, but you can create your own test runner.
Take a look at the source for the JMockit and Robolectric test runners:
Robolectric
JMockit
Of the two the Robolectric one is much more complicated, so we don't want to duplicate that functionality. The JMockit test runner is fairly simple. It should work to extend the RobolectricTestRunner and include the JMockit functionality.
import mockit.internal.startup.*;
class MyTestRunner extends RobolectricTestRunner {
static { Startup.initializeIfNeeded(); }
/**
* Constructs a new instance of the test runner.
*
* #throws InitializationError if the test class is malformed
*/
public MyTestRunner(Class<?> testClass) throws InitializationError
{
super(testClass);
}
}
At version Version 1.8 (Apr 27, 2014) JMockit can work in conjunction with Robolectric.
JMockit now works fine with the Robolectric Android testing tool (tested with Robolectric 2.2 and 2.3).
http://jmockit.org/changes.html