Android Espresso Test Suites - android

I am interested in how to run Espresso tests from command line (gradle task) individually (run group/suite tests then close app and then run another group/suite of tests).
Found that it is feasible to implement JUnit Test Suites but do not really understand how does it looks like under the hood in a context of instrumentation tests. Does it starts separate processes per Test suite? There is sample application on Github but how to execute it from terminal?
Another interesting discovery is Sharding tests. However, it one sentence documentation.
May be somebody can share with any experience of running Espresso tests individually.

Most of this is documented as part of AndroidJUnitRunner: https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html
The key piece that is missing is how to pass those parameters via Gradle. You can do that by specifying the options at the commandline as such:
./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=*The full name of your test suite goes here*

I would recommend using the Spoon library to run your tests individually or in parallel on multiple devices. You can either use the jar file or use the Spoon gradle plugin mentioned on the same page. Both of them have adequate documentation to help you set it up.
You can also use Spoon to run an individual test and the command would look something like this:
./gradlew yourSpoonTaskName -PspoonClassName=com.yourPackageName.blah.ClassName
-PspoonMethodName=methodName
In order to know what yourSpoonTaskName is run ./gradlew tasks.
Also, in your build.gradle file add the following spoon configuration:
spoon {
// for debug output
debug = true
// To grant permissions to Android M >= devices
grantAllPermissions = true
// for sharding
/*
this will execute tests in parallel on multiple devices.
*/
shard = true
// Add this to run a specific test class & method
if (project.hasProperty('spoonClassName')) {
className = project.spoonClassName
}
if (project.hasProperty('spoonMethodName')) {
methodName = project.spoonMethodName
}
}
If you are not interested in Spoon and just want a simple solution, then use the following command to run an individual test:
am instrument -w -r -e class com.packageName.blah.TestName#methodName com.packageName.blah.YourIntrumentationRunnerName
You can easily determine these values if you right click the test name in AndroidStudio and run it. In the console, you will see the entire command being printed when the test is bring run.

Related

Run specific instrumentation test suite with Gradle Managed Devices - Android

So with the release of Android Studio Dolphin & Beta of Electric Eel, I wanted to try the instrumentation tests in gradle. I do however want to exclude some of the tests being run, in order to be able to run specific test suites one at a time.
So here is what I configured so far:
android {
testOptions {
managedDevices {
devices {
pixel2api30 (com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Pixel 2"
apiLevel = 30
systemImageSource = "aosp-atd"
}
}
}
}
}
I know I can run my entire suite using
./gradlew device-nameBuildVariantAndroidTest
In my case that would be
./gradlew pixel2api30gaeDebugAndroidTest
gaeDebug being my build variant. This command is being run in my project root.
If I want to run the tests in the tests/large folder for example
How would I go about doing that? Thanks.
For this you could use 2 different approaches:
Create and run test suites.
For example, for each of these folders, create a TestSuite and define Test classes. For example:
#RunWith(Suite.class)
#Suite.SuiteClasses({
ExampleLargeTest.class,
ExampleTwoLargeTest.class,
ExampleThreeLargeTest.class
})
public class LargeTestsSuite {
}
This suite can be run using following command
./gradlew pixel2api30gaeDebugAndroidTest - Pandroid.testInstrumentationRunnerArguments.class=path.to.your.suite.class
Use Test Categories
Annotate your Test classes like this:
#Category("Large")
public class ExampleLargeTest { ... }
And then you could execute the following command for running all tests with same category:
./gradlew pixel2api30gaeDebugAndroidTest -PtestCategory=Large
Hopefully one of these two approaches will suite you.

Is it possible in to only run tests in a certain _directory_ via command line (in Android Studio/Gradle)?

I have a need to separate running "unit tests" from more specifically named unit tests called "requirements tests". Directory structure like:
src
|__main\
|__test\
|__testRequirements\
I added a separate srcSet so I can run these all together no problem.
sourceSets {
test {
java.srcDirs = ['src/test/java', 'src/testRequirements/java']
}
}
But, I need to be able to run ONLY the testRequirements by command so I can have ONLY them run in their own CI flow. I see that I can target them specifically by using the filtering by package/class/method a la:
./gradlew test --tests *RequirementsTest
But that relies on humans appending the same string to every class name.
I played around a little with creating a new flavor just for requirements so I could access with a command ./gradlew testRequirementsDebugUnitTest but that would run both the requirements AND all my other unit tests

Using Gradle Spoon Plugin to launch tests in specific location

I am having problem to find documentation how could I solve that case.
I am capable of launching small/medium/large tests with:
./gradlew spoonSmall
./gradlew spoonMedium
./gradlew spoonLarge
Or launching specific tests with usage of this setup:
spoon {
(...)
if (project.hasProperty('spoonClassName')) {
className = project.spoonClassName
if (project.hasProperty('spoonMethodName')) {
methodName = project.spoonMethodName
}
}
}
I can launch specific file:
./gradlew spoon -PspoonClassName=com.package.tests.MyTest;
What I am interested in is a possibility to launch all tests located in:
./gradlew spoon -PspoonClassName=com.package.tests
package. Either method is fine. Some parameter to bash console or maybe way to create my own annotation and launch by something like ./gradlew spoonMyTests.
I am grateful for suggestions/help.
From the official docs:
There are numerous ways to run a specific test, or set of tests. You
can use the Spoon --size, --class-name or --method-name options, or
you can use the --e option to pass arguments to the instrumentation
runner, e.g.
--e package=com.mypackage.unit_tests
The following command should work when executing Spoon directly from command line (not from the gradle task)
java -jar spoon-runner-1.1.9-jar-with-dependencies.jar \
--apk ExampleApp-debug.apk \
--test-apk ExampleApp-debug-androidTest-unaligned.apk \
--e package=com.package.tests
You need to find a way to pass this extra param to the Spoon gradle plugin.
UPDATE
From the official doc of Spoon Gradle plugin:
Custom instrumentation arguments
Use the instrumentationArgs property on spoon extension to pass custom
parameters to your tests:
spoon { instrumentationArgs = ["foo=bar", "name=value"] }
In your case, this should look like the following:
spoon {
instrumentationArgs = ["package=com.package.tests"]
}

Running a specific instrumentation unit test with Gradle

Is there a way to run a specific Android instrumentation unit test using Gradle? I've tried
gradle -Dtest.single=UnitTestName connectedInstrumentTest
but it seems to run all the tests in the package.
Using test.single appears to be deprecated. The new correct way to do this is
./gradlew :<module>:test --tests <pattern>
where <pattern> could be something like:
com.example.MyTest to run all test methods in com.example.MyTest
*MyTest to match every method in every class whose name ends with MyTest
*.MyTest.myMethod to run a specific test method in class MyTest in any package
If you have a multi-project build, make sure to give the module path before the test task; otherwise you'll get a misleading error message when it searches for your test pattern in every subproject.
None of this is documented on the Gradle site anywhere I could find it.
This works if you're using an instrumentationTestRunner:
./gradlew test -Pandroid.testInstrumentationRunnerArguments.class=<pkg>.YourClassName
Using gradle 2.10 and android gradle plugin 2.0.0-beta2.
Since you know what test(s) you want to run, you probably know which module / flavor to use too. You can help Gradle out by specifying the exact module and Gradle task. So if your test is in the app module and you want to test the debug flavor:
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=<pkg>.YourClassName
You can get even more fancy with the tests_regex argument instead:
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.tests_regex=PartialClassName*
./gradlew app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.tests_regex=partialMethodName*
The pattern is -D<testTaskName>.single=<TestClass> so in your example it should be:
gradle -DconnectedInstrumentTest.single=UnitTestName connectedInstrumentTest
NOTE: This answer is outdated. You should use the --tests switch in the latest versions of Gradle. (see other answers for an explanation)
Since Android gradle plugin 1.1.0-rc1, one can run single test class using --tests flag by executing:
./gradlew app:testDebug --tests=com.example.MyTest
See http://tools.android.com/tech-docs/unit-testing-support#TOC-Running-from-Gradle
You gotta check this out.
https://github.com/JCAndKSolutions/android-unit-test
I made an issue in this github repository, and this guy solved my problem and upload to maven, so in my build.gradle file I use this plugin.
Instructions are written in his repository. you can easily follow it.
After using this android-unit-test plugin, I can use like
../gradlew -Dtest.single=SomeTest test
or
../gradlew -Dtest.single=SomeTest clean check
Now it's working and I could only run the specific tests I want to
You should not forget to specify a build variant name after test property declaration like
-Dtest<buildVariantName>=<yourTestName>.
Like if you have a debug build type which gives you debug variant after compilation, then if you want to run a test only for this build variant you should declare a command like this:
./gradlew -DtestDebug=UnitTestName testDebug
Erdi's answer didn't work for me but I have a single parent for all my test classes so I was able to do this:
public abstract class BaseEspressoTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
//...
#Override
protected void runTest() throws Throwable {
if(getClass().getSimpleName().equals("MyTestClassName")) {
super.runTest();
}
}
//...
}
This executes only MyTestClassName. We can extend it further to execute only specific test method (or methods):
public abstract class BaseEspressoTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
//...
#Override
protected void runTest() throws Throwable {
if("MyTestClassName".equals(getClass().getSimpleName())
&& "testMethodName".equals(getName())) {
super.runTest();
}
}
//...
}
the Gradle command does not work for me.
I used below mentioened adb command.
for this you need to build your apk first.
adb shell am instrument -w -r -e package -e debug false .debug.test/android.support.test.runner.AndroidJUnitRunner

Parameterized JUnit tests in Android test project

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.

Categories

Resources