Android Test Orchestrator and custom Application class - android

I gave a try to Android Test Orchestrator and it doesn't see any tests if Application class were changed. Pretty easy to reproduce.
In Android Studio 3 preview Beta 6 create simple project with wizard
Create custom runner like:
class CustomTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApplicationClass::class.simpleName, context)
}
}
Replace instrumentation runner with new one
No test found after running instrumented tests
Any ideas? Looks like the Orchestrator depends on application class name from manifest.
I use this configuration to use special Dagger dependencies for tests.

I had the similar issue, with a custom test runner. Make sure that your TestApplicationClass does not crash at runtime. If the custom runner crashes then orchestrator will not be able to get information about the tests and will return the message:
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack #Test annotations).
That is what happened to me in my custom runner.
Good luck!

Related

Issues using custom testInstrumentationRunner

I've not found any post on Stack Overflow that helped, so I'm posting my own question.
I have a custom testInstrumentationRunner that when set doesn't run any tests.
The logs says:
App restart successful without requiring a re-install.
Running tests
adb shell am instrument -w -m --no-window-animation -e debug false -e class 'com.my.app.MyFragmentTest' com.my.app.debug.test/com.my.app.CustomTestRunner
Connected to process on device
When running the normal androidx.test.runner.AndroidJUnitRunner the tests run, but fail, because I need the custom runner.
The runner is set up like the androidx one, in:
defaultConfig {
// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner
testInstrumentationRunner "com.my.app.CustomTestRunner"
}
It's also located in the com.my.app package in androidTest-folder
The code for the runner is:
package com.my.app
import ...
class CustomTestRunner: AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
DexOpener.install(this)
Timber.i("This should print hopefully")
return super.newApplication(cl, ApplicationTest::class.java.canonicalName, context)
}
}
Not sure if it matters, but here's the code for the application class, which extends the application class used for the app itself:
package com.my.app
class ApplicationTest: MyApplication() {
override fun onCreate() {
super.onCreate()
appContext = applicationContext
initializeDependencyInjection()
}
override fun initializeDependencyInjection() {}
}
I have a test-case I'm trying to run, which contains 7 tests. The androidx runner fails on 7/7 tests. But when I run with the custom runner, I get duration 0ms and 0/0 tests.
So it seems like Android isn't detecting this runner. I also get the same results using a random name in the testInstrumentationRunner, like testInstrumentationRunner "not.a.TestRunner"
I'm sure you've already solved the issue or switched jobs to growing potatoes or something more productive, but I just spent my whole morning debugging this same exact scenario and managed to find a solution:
The custom instrumentation runner you have is not ignored, but there is something broken in your custom Test Application initialization. Probably in your replacement dependency injections, like it was in my case. When you run the tests using the custom runner, the custom Test Application silently crashes immediately, but this is not indicated in the test results or in any other easily visible place. The crash stack trace is, however, accessible in logcat. So solve that first, and then try using your custom test runner again.
Do not mix between Instrumentation Tests and Unit Tests:
Unit Tests
Unit tests runs in local JVM and minimizes the execution time.
Unit Tests cannot test UI of the app without mocking activity objects.
Unit test are used for white box testing for testing code.
Most of the time Unit Tests are written by Developers.
You don’t need a device connected for execution of Unit Tests.
Instrumentation Tests
Instrumentation tests are used for black box testing.
It is used to test GUI of the application along with its functionality in real environment.
For execution of Instrumentation tests you need a device /emulator on which the application is first installed and then tests are executed.
Automation Testers are involved in writing instrumentation framework.
When testing a library module, you can link the test application in src/androidTest/AndroidManifest.xml:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="com.my.app.ApplicationTest"/>
</manifest>

How mock Companion object in Android using Mockito

I been trying to run a unite test and I am now facing some issue in mocking the Application Context. I tried mockStatic() but is not working. I am using Junit 5 and org.mockito:mockito-inline:3.4.6 for testing.
class ApplicationContext : Application() {
init {
instance = this
}
companion object {
private var instance : ApplicationContext? = null
fun applicationContext() : Context = instance!!.applicationContext
}
When I run my test this throws a NullPointerException.
java.lang.NullPointerException
at com.adaptavant.yoco.Util.ApplicationContext$Companion.applicationContext(ApplicationContext.kt:14)
at com.adaptavant.yoco.viewModel.LoginViewModelTest.setUp(LoginViewModelTest.kt:48)
Can Someone help me out here.
There is no ApplicationContext available in unit test - you need to use instrumentation tests for that:
Local unit tests:
Use these tests to minimize execution time when your tests have no Android framework dependencies or when you can mock the Android framework dependencies.
Instrumented tests
These tests have access to Instrumentation APIs, give you access to information such as the Context of the app you are testing, and let you control the app under test from your test code.
Source

Failing to run Instrumented tests on a new Android Kotlin project

I just created a new android application with Kotlin Support.
When I've tried to run the default instrumented tests it does not run and shows me this message:
Class not found: "oussaki.com.pos.ExampleInstrumentedTest"Empty test suite.
This the Instrumented test class that I'm trying to run:
#RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
#Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("oussaki.com.pos", appContext.packageName)
}
}
This is a known issue: https://issuetracker.google.com/issues/38452937 which hopefully will be fixed in the next release.
For the time being you can manually go to 'Edit Configurations' and add the configuration for the specific class/method you want to run under 'Android Instrumented Tests'.
You could also try the latest canary build: https://developer.android.com/studio/preview/index.html but personally I've had trouble getting it to work with my project.

Is there a way to write a unit test for a target API

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

Can I use Robolectric to unit test static library jars?

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

Categories

Resources