Android, run regular app in instrumentation mode (adb + debug only) - android

I'd like to run an Android app in a way that it can access InstrumentationRegistry.getInstrumentation() during the runtime.
This is only for debug builds and installing the app through ADB is sufficient. Because I don't want to use the instrumentation test-suite, I don't want to use InstrumentationTestRunner but launch the MainActivity of the app directly.
How is this possible?
What I've tried:
Added regular implementation dependency on test packages (instead of test only dependency) - worked
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.test:runner:1.1.0'
Added instrumentation to manifest
<instrumentation android:name=".MainActivity"
android:targetPackage="com.android.shell"
android:label="App" />
(Not sure whether this is actually correct.)
Ran the app using (app already installed on the device)
adb shell am instrument -w com.example.app/.MainActivity
This results in:
android.util.AndroidException: INSTRUMENTATION_FAILED: com.example.app/com.example.app.MainActivity
at com.android.commands.am.Instrument.run(Instrument.java:519)
at com.android.commands.am.Am.runInstrument(Am.java:202)
at com.android.commands.am.Am.onRun(Am.java:80)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:60)
at com.android.commands.am.Am.main(Am.java:50)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:399)
So essentially my question is, how do I achieve running the app in a way I can use UiAutomator during the runtime?
Thanks in advance!

Yes, it is possible:
1. Add the dependencies to your build.gradle. It should be like this:
implementation 'androidx.test.ext:junit:1.1.2'
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.test:runner:1.1.0'
2. Add the following uses-library inside application tag and instrumentation inside manifest in you AndroidManifest.xml:
<!-- Add inside application tag -->
<uses-library android:name="android.test.runner" />
Set android:targetPackage to your own application package name.
<!-- Add inside manifest tag -->
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="App"
android:targetPackage="com.example.app" />
</manifest>
3. Create your activity. You can use InstrumentationRegistry.getInstrumentation(); there.
4. Create you Test class in the same source set of your application classes (inside src/main/java)
#RunWith(AndroidJUnit4.class)
public class Test {
#org.junit.Test
public void test() {
// Put any code here. It is launching the activity.
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
Context context = instrumentation.getTargetContext();
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
5. Build and install the app.
6. Run the app using: adb shell am instrument -w -m -e debug false -e class 'com.example.app.Test' com.example.app/androidx.test.runner.AndroidJUnitRunner
7. (Optional) If you want to execute it directly from the run button in Android Studio, then add this inside android in your build.gradle, it will change the place where the test classes must be created, pointing to the same folder as the main code:
sourceSets {
androidTest {
java.srcDir 'src/main/java'
}
}

Related

Android Studio - Junit 4 - Running code before ALL tests

I have an app to which I want to force the locale whenever the app is opened. So I will use Locale.setDefault(myCountryLocale). in the application's onCreate() method
Unit Tests don't start that class, so I want to be able to set the same locale whenever tests are started. Either by an extension or gradle script to setup the locale so that tests don't fail if whoever runs them uses another locale.
I know this is kinda old post but I faced this problem.
You can extends RunListener class and override the testRunStarted this code block will run only once before all your tests.
This listener will be added to you test manifest like this
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="YOUR_APP_PACKAGE">
<meta-data
android:name="listener"
android:value="YOUR_LISTENER_PACKAGE" />
</instrumentation>

Unable to invoke Cucumber Tests for Android app in Java

I wanted to run cucumber tests. I am unable to invoke or to run these tests. This is what I have done:
In app/build.gradle
defaultConfig {
testApplicationId "com.my.app.test"
testInstrumentationRunner "com.my.app.test.CucumberInstrumentation"
}
sourceSets {
androidTest { assets.srcDirs = ['src/androidTest/assets'] }
}
buildTypes {
debug {
testCoverageEnabled true
buildConfigField "String", "TEST_TAGS", "\"${getTestTags()}\""
}
}
def getTestTags() {
return project.hasProperty("tags") ? project.getProperties().get("tags") : ""
}
dependencies {
androidTestImplementation 'info.cukes:cucumber-android:1.2.5'
androidTestImplementation 'info.cukes:cucumber-picocontainer:1.2.5'
}
This is my feature file under the src/androidTest/assets/features directory.
Feature: Enter login details
#login-feature
Scenario Outline: Successful Login
Given App is launch and user is not logged in
When I Click on Log in with Email button
And Login screen is launched
And I input an email, "<Email>"
And I input a password, "<Password>"
And I press on Log in button
Then I Should get logged in and redirect to home screen
Examples:
| Email | Password |
| user#login.com | mypasword123 |
This is my Login StepDefinitions file under the src/androidTest/java/com/my/app/test directory.
class LoginStepdefs {
#Rule
private ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule<>(LoginActivity.class);
private LoginActivity activity;
#Before("#login-feature")
public void setUp() {
activityTestRule.launchActivity(new Intent());
activity = activityTestRule.getActivity();
}
#After("#login-feature")
public void tearDown() {
activityTestRule.getActivity().finish();
}
#Given("^App is launch and user is not logged in$")
public void appIsLaunchAndUserIsNotLoggedIn() throws Throwable {
System.out.println("appIsLaunchAndUserIsNotLoggedIn");
}
// and other functions
}
Then this is my Runner file.
#CucumberOptions(
features = "features",
glue = "com.my.app.test")
public class CucumberInstrumentation extends MonitoringInstrumentation {
private final CucumberInstrumentationCore instrumentationCore = new CucumberInstrumentationCore(this);
#Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
String tags = BuildConfig.TEST_TAGS;
if (!tags.isEmpty()) {
arguments.putString("tags", tags.replaceAll(",", "--").replaceAll("\\s",""));
}
instrumentationCore.create(arguments);
start();
}
#Override
public void onStart() {
super.onStart();
waitForIdleSync();
instrumentationCore.start();
}
}
And under AndroidManifest file. i added this
<application
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="cucumber.api.android.CucumberInstrumentation"
android:targetPackage="com.my.app.test" />
Now I am trying to run the cucumber tests with Android Studio like this:
Open “Edit Configuration”
Click + on left panel and select “Android Instrumented Tests”
Put a name you like to remember with at the Name field on top and select OK.
Click “Run”
As a result this is the console output. I have also checked it by using break points, where the debugger never stops.
Testing started at 6:24 PM ...
02/12 18:24:35: Launching CucumberTests
$ adb push /home/sajid/Git/app-android/app/build/outputs/apk/debug/app-debug.apk /data/local/tmp/com.my.app
$ adb shell pm install -t -r "/data/local/tmp/com.my.app"
pkg: /data/local/tmp/com.my.app
Success
APK installed in 2 s 118 ms
$ adb push /home/sajid/Git/app-android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk /data/local/tmp/com.my.app.test
$ adb shell pm install -t -r "/data/local/tmp/com.my.app.test"
pkg: /data/local/tmp/com.my.app.test
Success
APK installed in 738 ms
Running tests
$ adb shell am instrument -w -r -e debug false com.my.app.test/com.my.app.test.CucumberInstrumentation
Client not ready yet..
Started running tests
Tests ran to completion.
Test running failed: Unable to find instrumentation info for: ComponentInfo{com.my.app.test/cucumber.api.android.CucumberInstrumentation}
Empty test suite.
Please guide me where I am going wrong and what I need to make this work.
EDIT:
Your Manifest should be something like this
<application
android:allowBackup="true"
android:label="#string/app_name">
<activity
android:name=".LoginActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
Obs: Don't need to put instrumentation here if you put in the Gradle.
OLD:
Check on your file "CucumberInstrumentation" the "package" path
Something like com.my.app.test
Copy this path and past it on your app/build.gradle. Changing the com.my.app.test for your real path, like com.sajid.app.test
I don't know what is the reason. But in my feature file
When I changed "Scenario Outline:" to "Scenario:"
its start working

Android Espresso Test Suites

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.

Android Studio: grant permission between installing test APK and running tests with graphical test runner

I'm trying to automate the disabling of animations as described in this post, but that only seems to work for command-line invocation of connectedAndroidTest. I want to use the graphical test runner in Studio, with the list box showing passed/failed tests. With that runner, the permission grant (adb shell pm grant ... android.permission.SET_ANIMATION_SCALE) is never run, seemingly because the gradle task installDebugAndroidTest is never run, instead the runner is running Gradle as far as assembleDebugAndroidTest (or whatever alternate gradle task I specify in my run configuration), and then installing com.mypackage.test by some other (non-Gradle?) method immediately before running tests. So any prior permission grant is reset by that installation.
How can I grant SET_ANIMATION_SCALE between the graphical test runner's installation of the test package and the running of the test?
You can do it using reflection, adding the permission to the manifest, creating an Espresso TestRule and a task (explained here in detail).
Add the permission to the manifest of a debug/mock variant:
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>
Create your own task depending on installDebug and make connectedDebugAndroidTest depend on your task. You also need to grant the SET_ANIMATION_SCALE permission for testing.
Create a test rule that uses internally reflection to retrieve and restore animation scales (code):
public class AnimationAwareWonderTestRule extends AnimationAwareAwesomeTestRule {
private float[] mAnimationScales;
#Override
protected void before() throws Throwable {
mAnimationScales = AnimationAwareWonder.tryToRetrieveAndDisableAnimationsAndTransitions();
}
#Override
protected void after() throws Throwable {
AnimationAwareWonder.tryToRestoreAndEnableAnimationsAndTransitions(mAnimationScales);
}
}
It works but seems it's not possible at the moment to use this permission in MarshMallow.

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

Categories

Resources