Unable to invoke Cucumber Tests for Android app in Java - android

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

Related

Fragment Test hangs and then fails

I have the following test for Android:
#RunWith(AndroidJUnit4::class)
class LogoutFragmentTest {
#Test
fun testEventFragment() {
val scenario = launchFragment<LogoutFragment>(
initialState = Lifecycle.State.INITIALIZED
)
scenario.onFragment { fragment ->
assertThat(fragment.m_Context).isNotNull()
}
}
}
I am launching the test from the androidTest folder (with right mouse click)in the project with the option 'Run All tests'. The test launches an emulator, performs some tasks, it announces 'Connected to process .. on device ..' and then starts the task connectedDebugAndroidTests and remains so for a while.
Then it displays a message explaining where the test results are.
Test results saved as file ...
In the test error log I have the following information:
INSTRUMENTATION_RESULT: stream=
Test results for InstrumentationTestRunner=
Time: 0.0
OK (0 tests)
INSTRUMENTATION_CODE: -1
Can anyone please help ?
I have managed to solve this:
I needed to configure the runner in the build.gradle file:
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

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

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'
}
}

Android espresso console log Build.Serial onFail

I am running test on multiple devices at once using the adb test command. My pseudo shell script looks like this:
for each device
adb -s ${device} shell am instrument -w -e ${classOrPkg} ${androidTestPackage}${test_name} ${main_package}.${flavor}.test/android.support.test.runner.AndroidJUnitRunner &
The problem is when a test fails, I have no information on which device the failure occurred. I could use LogCat but it requires looking up logcat for each device. And also, System.out.println() does not work.
One possible solution I am trying right now is by extending TestWatcher class and overriding the failed() method like this,
public class TestWatcherRule extends TestWatcher {
#Override
protected void failed(Throwable e, Description description) {
Description d = Description.createTestDescription(e.getClass(), "<<<< Failed on Device: " + Build.SERIAL);
super.failed(e, d);
}
}
Implementation:
#Rule
public TestWatcher testWatcher = new TestWatcherRule();
assertThat("My message", true, is(false));
I cannot get the device serial yet on the terminal.
My expected output would be something like this:
com.myapp.mobile.tests.BenefitCardDBTest:
Error in addDeleteCard(com.myapp.mobile.tests.BenefitCardDBTest):
**<<<< Failed on Device: HTC10xwrtxe**
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.myapp.mobile.qa:id/drawer_layout
Let's say this is my sample Espresso test:
#RunWith(AndroidJUnit4.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SettingsActivityTest {
#Rule
public ActivityTestRule<SettingsActivity> mRule = new ActivityTestRule<>(SettingsActivity.class);
#Test
public void checkIfToolbarIsProperlyDisplayed() throws InterruptedException {
onView(withText(R.string.action_settings)).check(matches(withParent(withId(R.id.toolbar))));
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
}
}
To run it on multiple devices I'm using Gradle's connectedAndroidTest which extends connectedCheck, so it :
will run on all connected devices in parallel.
From:
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Android-tasks
Just go use your terminal or console to go to your project's directory, then use:
./gradlew connectedAndroidTest.
This one is very useful as it would generate HTML test output which allows you to check which method on which device had failed.
It would look like this:
Hope it will help

Grant ACCESS_MOCK_LOCATION permission for testing using adb fails in Marshmallow

Here is the recipe of the problem:
Add mock location permission in debug manifest <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
Install debug version of application in phone (in Gradle: debug { debuggable true })
Try to give permission with adb: adb shell pm grant com.myapp.name android.permission.ACCESS_MOCK_LOCATION
The outcome of the command is:
Operation not allowed: java.lang.SecurityException: Permission android.permission.ACCESS_MOCK_LOCATION is not a changeable permission type"
If I go to developer options on the phone and set Setting >> Developer Option >> Select Mock location app it works.
I need this for automated tests, so obviously the option of going to the phone settings is not valid because it's reset on every installation of the app, so I need the adb option to work.
I found the solution inside the Calabash fix for this same problem:
https://github.com/calabash/calabash-android/commit/b31be97953325383b9295ff52234a0121cc27e27
adb shell appops set com.myapp.name 58 allow
To do this automatically from gradle you can add the command to the install tasks:
def adb = android.getAdbExe().toString()
tasks.whenTaskAdded { task ->
if (task.name.startsWith('install')) {
task.doLast {
android.applicationVariants.all { variant ->
"${adb} devices".execute().text.eachLine {
if (it.endsWith("device")) {
def device = it.split()[0]
println "Granting test permissions on device ${device}\n"
"${adb} shell appops set ${variant.applicationId} 58 allow".execute()
}
}
}
}
}
}
But you have to explictly call the install task before the connectedTest task, like:
gradlew installMyAppDebug connectedMyAppDebugAndroidTest
Alternatively, you can use Test Butler plugin to manage test permissions among other things https://github.com/linkedin/test-butler
Example project where I configured and use Test Butler: https://github.com/sebaslogen/Blendletje/
public class TestRunner extends AndroidJUnitRunner {
#Override
public void onStart() {
TestButler.setup(InstrumentationRegistry.getTargetContext());
super.onStart();
}
#Override
public void finish(final int resultCode, final Bundle results) {
TestButler.teardown(InstrumentationRegistry.getTargetContext());
super.finish(resultCode, results);
}
}
#Before
public void setUp() throws Exception {
final Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
TestButler.verifyAnimationsDisabled(targetContext);
}
You can see how it's installed automatically when running tests: https://github.com/sebaslogen/Blendletje/blob/master/app/build.gradle#L107

Test running startedTest running failed: Unable to find instrumentation info for: ComponentInfo{ro.vst.test/android.test.InstrumentationTestRunner}

here is my full code, when i run following code,
public class KernelTest {
#Test
public void testM() {
assertEquals(1, 1);
}
}
It raise:
Test running failed: Unable to find instrumentation info for: ComponentInfo{ro.vst.test/android.test.InstrumentationTestRunner}
I've found the solution: in android studio 1.5, whether I specify runner or not, avd side will generate a package name with "my_pkg_name.test/runner_name", the solution steps are:
run android test once
I get "Test running failed: Unable to find instrumentation info for: ComponentInfo{ro.vst.test/android.test.InstrumentationTestRunner}"
run adb shell pm list instrumentation, find pkg relative instrument
I get "instrumentation:ro.vst.test/com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
the two TestRunner are different, so I specify TestRunner in build.gradle like following
build.gradle
defaultConfig {
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
now run again, it should work

Categories

Resources