Espresso testing in separate module - android

I have the android application and I want to use Espresso framework to create test automation tools and tests. But I don't want to create something in my app and want separate module with Espresso that will start my app and test it using Espresso. I use Android Studio. So.. Have you any ideas how to solve this problem?

You can use a library module specialised for instrumentation tests. Basically just create a library module but use a different plugin:
apply plugin: 'com.android.test' // A plugin used for test-only-modules
Also you need to changep your android part in your build.gradle file in your test module a little:
android {
[...]
defaultConfig {
[...]
testApplicationId "com.yourcompany.yourapp.test"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
[...]
// Set the target app project.
// The module specified here should contain the
// production code test should run against.
targetProjectPath ':app'
}
Then add your app and all your espresso libraries etc. in your test module's dependencies:
dependencies {
implementation project(':app')
// All your test dependencies etc.
implementation "androidx.test.espresso:espresso-core:$espresso_version"
}
Using a gradle sync and run tests you can then execute your tests locally. You'll also get all your normal gradle tasks for that module which you can use to assemble your test APK etc. if you need it to test externally e.g. in Firebase Test Labs.

Related

Shared Custom AndroidJUnitRunner for multiple Android Library modules

Description
I'm trying to create a custom AndroidJUnitRunner that can be reused by multiple Android Library modules in my project for my Hilt Android tests so I don't have to keep creating a new one in each module.
Project Setup
I have a custom Android Library gradle config that is responsible for configuring everything related to Android.
android-library-config.gradle:
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
android {
// android related configuration
}
// additional configuration that should apply to ALL android library modules
Then, every Android Library module applies this gradle config instead of defining its own configuration:
some-feature-module/build.gradle:
apply from: "$rootProject.projectDir/android-library-config.gradle"
android {
// additional setup if needed
}
dependencies {
implementation projects.someLibraryInThisProject
}
Adding Hilt Android UI Tests to an Android Library module
Following the Hilt testing documentation for instrumented tests:
To use the Hilt test application in instrumented tests, you need to configure a new test runner. This makes Hilt work for all of the instrumented tests in your project. Perform the following steps:
Create a custom class that extends AndroidJUnitRunner in the
androidTest folder.
Override the newApplication function and pass in the name of the
generated Hilt test application.
With the following example in the androidTest directory:
// A custom runner to set up the instrumented application class for tests.
class CustomTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
Then, we need to configure the new test runner in our gradle file:
android {
defaultConfig {
// Replace com.example.android.dagger with your class path.
testInstrumentationRunner "com.example.android.dagger.CustomTestRunner"
}
}
However, I don't want to have to create a new CustomTestRunner for every Android Library module in my project. Instead, I want to create one and reference it only in my android-library-config.gradle so that it gets applied to each module that applies that config.
What I've tried
I've tried introducing a new Android Library module, :library:ui-testing:common, and adding the custom test runner in its androidTest directory:
library
└─ ui-testing
└─ common
└─ src
└─ androidTest
└─ java
└─ com.uitesting.common.CustomTestRunner
And then, updated my android-library-config.gradle to reference this new test runner:
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
android {
defaultConfig {
// Replace com.example.android.dagger with your class path.
testInstrumentationRunner "com.uitesting.common.CustomTestRunner"
}
}
dependencies {
implementation projects.library.uiTesting.common
}
Since :some-feature-module/build.gradle applies android-library-config.gradle I expect my Instrumented tests defined in this module to use this new custom test runner. Instead, my tests just don't run. I get the following output:
Starting 0 tests on Pixel 3 - 10
I've also tried having :some-feature-module directly depend on :library:ui-testing:common and adding the following to :some-feature-module/build.gradle:
android {
defaultConfig {
// Replace com.example.android.dagger with your class path.
testInstrumentationRunner "com.uitesting.common.CustomTestRunner"
}
}
and same result.
I'm wondering if this is even possible since I haven't found any examples of this in the official Android Instrumented test documentation.

Modularised Android testing - module test using base module test

I have a android project with multiple modules, module B that depends on module A
dependencies {
implementation project(':features:A')
}
I want unit test in module B to extend base test from module A. An IDE resolved all dependencies successfully, but in build time only production code from module A is accessable in test.
I tried to add dependency specificly for tests:
dependencies {
testCompile project(':features:A').sourceSets.test.output
}
This results an error:
Could not get unknown property 'test' for SourceSet container of type org.gradle.api.internal.tasks.DefaultSourceSetContainer.
Same if I'm trying to create custom artifact with tests.
How can I get access to test-specific code in base module from dependent module?
AS 3.1, gradle 4.1
The best solution I found is to create third module just for tests, put my base test classes and test dependencies there.
And use it in both module A and module B (and all other modules - just one module for all unit test dependencies)
testImplementation project(':features:basetests')
Disadvantage - it is possible to import it in non-test artifacts (e.q. implementation project(':features:basetests')). Good code review can protect from such mistakes.
Looks like there is no convenient way to define base shared test-only code.

Android Studio can't see dagger 2 test component

I` am trying to create some extra Dagger 2 modules for android instrumental tests. And Dagger 2 files are generated successfully, but my Android Studio IDE can't see it. I tried to use android-apt plug-in, gradle-apt-plagin and native android annotation processor. Nevertheless, nothing worked. However gradle 2 file for debug or prod application working fine.
For test module and component I use some custom sours folder:
def commonTestDir = 'src/commonTest/java'
test {
java.srcDir commonTestDir
}
androidTest {
java.srcDir commonTestDir
}
I use all of compile/apt, testCompile/testApt, androidTestCompile/androidTestApt for daggger 2 declaration in build.gradle file.
p.s. I`am sorry for my bad english...

Gradle Android testing

I'm new to gradle and Android Studio, and I'm trying to figure out how to run tests. I followed instructions in http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing and I'm able to run Instrument Tests but only executing /gradlew connectedInstrumentTest.
I'm having troubles understanding the other check tasks, that don't execute any code in my app.
This is what ./gradlew tasks prints
...
Verification tasks
------------------
check - Runs all checks.
connectedCheck - Runs all device checks on currently connected devices.
connectedInstrumentTest - Installs and runs the tests for Build 'Debug' on connected devices.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
...
If check runs all checks... shouldn't it run the ones I get from connectedInstrumentTest?
Also, how can I run tests that don't need the Android environment? Should I place them under /test/java ? If so, what command should I run to execute them?
Sorry if these questions seem very obvious, but I just haven't been able to find any answer to these questions in the docs.
Thank you!
Edit:
So I have made some progress here.
It looks like check is not doing anything. It would be (right now) up the developer to add dependant tasks to check to run some JUnit tests. You will need to have to create a task, make it find the sources, compile them and run them.
connectedCheckand connectedInstrumentTest: runs instrumentationTest in the device. (this always worked).
deviceCheck: This is useful, as the docs say, for Continuos integration testing.
If check runs all checks... shouldn't it run the ones I get from connectedInstrumentTest?
you can have to run connectedInstrumentTest task as dependency to check task.
check.dependsOn connectedInstrumentTest
How can I run tests that don't need the Android environment?
For android projects we can discussed about 3 types of Tests
Junit Test
Robolectric Test
Instrument Test
Junit test
We can't use plain junit test to check android related classes. What we can do is separate core java classes to a java library project and add that dependency to android project.
Robolectric Test
We can use robolectric test to run the unit tests outside of the Emulator. This makes the tests fast and easy to configure with CI servers.
To run robolectric test we use gradle-android-test-plugin
It clearly describes how to use that plugin.
Project structure
We have to use default folder structure in order to use this plugin. We have to use folder called 'test' to keep robolectric tests:
MyProject/
| settings.gradle
| build.gradle
- app/
| build.gradle
-main
-java
-com.example.calculator
-test
-java
-com.example.calculator.robolectrictests
build file is
buildscript {
repositories {
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.1-SNAPSHOT'
}
apply plugin: 'android'
apply plugin: 'android-test'
repositories {
mavenCentral()
}
android {
compileSdkVersion 19
buildToolsVersion "19.0.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 19
}
}
dependencies {
compile 'com.android.support:appcompat-v7:+'
testCompile 'junit:junit:4.10'
testCompile 'org.robolectric:robolectric:2.1.+'
testCompile 'com.squareup:fest-android:1.0.+'
}
This test task will automatically executes with the check task.
Instrument Test
As you mentioned, this requires using the android emulator. That makes the tests slow, meaning that they're not a good way to do TDD.
We can use robolectric test as unit tests in the TDD process.
Instrument test we can use as integrated test in the TDD.
check SHOULD run instrumentation tests IIRC. About running non android tests. Currently the android-gradle plugin doesn't support it. I made a plugin however to run JUnit tests with robolectric so you don't need instrumentation tests. Here is the link: android-unit-test

How to do Integration Testing on Android with the new Gradle Build System?

Our Android app needs automated testing, and our group is using Robotium to handle that for us. This is no problem for unit tests, but we're also writing a set of end-to-end integration tests to exercise not only the client by the back-end servers as well. I've got some tests that do this, but if possible, I'd like to break them out separately from the unit tests so that our continuous integration builds don't require a live server to be running in order to complete.
We're using the shiny new Gradle build system. I'm wondering if I could do something like a test-only flavor or a subproject that depends on the parent APK to make it go. I tried making this work with a separate project altogether using the Robotium instructions for testing a source-less debug APK, but it didn't work. Maybe because I was on real hardware and not an emulator. I've had poor luck with the emulator, even with the hardware acceleration installed.
Any advice, or should I just hold my breath and roll with my builds requiring the integration server to be available when builds are happening?
According to their Maven instructions all you need to do is add robotium-solo as a compile dependency.
repositories {
mavenCentral()
}
dependencies {
instrumentTestCompile 'com.jayway.android.robotium:robotium-solo:4.2'
}
This will ensure you have the robotium-solo.jar file in your classpath. Then define your tests in the src/instrumentTest directory and run gradle build. See if that works?
I'll help where I can, as we converted from maven to gradle about a year ago.
*EDIT OP wanted the tests to run separately from a gradle build, so the solution is to specify a custom source set like so:
sourceSets {
integrationTest {
// Gives you access to the compiled classes in your tests
compileClasspath += main.output
runtimeClasspath += main.output
}
}
dependencies {
integrationTestCompile 'com.jayway.android.robotium:robotium-solo:4.2'
}
// To run the tests: ./gradlew integrationTest
task integrationTest(type: Test) {
testClassesDir = sourceSests.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
Note: I don't have the android SDK installed on this computer. If main.output does not work try it with andriod.sourceSets.main.output and see if that works.

Categories

Resources