Following Android docs on unit testing and failing - android

I am trying to run the simple unit test by following the example here:
https://developer.android.com/training/testing/unit-testing/local-unit-tests
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import static com.google.common.truth.Truth.assertThat;
public class UnitTestSampleJava {
private static final String FAKE_STRING = "HELLO_WORLD";
private Context context = ApplicationProvider.getApplicationContext();
#Test
public void readStringFromContext_LocalizedString() {
// Given a Context object retrieved from Robolectric...
ClassUnderTest myObjectUnderTest = new ClassUnderTest(context);
// ...when the string is returned from the object under test...
String result = myObjectUnderTest.getHelloWorldString();
// ...then the result should be the expected one.
assertThat(result).isEqualTo(FAKE_STRING);
}
}
I have a brand new project and I set the gradle files as specified, then I created a test with this line:
private Context context = ApplicationProvider.getApplicationContext();
and I get an exception on that line number stating:
java.lang.IllegalStateException: No instrumentation registered! Must run under a registering instrumentation.
However, this was listed in the docs as a local unit test and not an instrumented test.

This will be common knowledge for experienced people, but I will write this for those who are just starting out like me.
A lot of the only tutorials were very confusing and I could not get them to compile or work because of different versions of everything.
The first thing I didn't realize is that there are two different Gradle functions, testImplementation and androidTestImplementation. The function "testImplementation" is for plain unit tests and the function "androidTestImplementation" is for instrumented unit tests (unit test but running on a physical device).
So when you see the command in Gradle under dependencies:
testImplementation 'junit:junit:4.12'
That's only including JUnit 4.12 for unit tests in the default app/src/test folder, not the app/src/androidTest folder.
If you follow the tutorial I linked above (which is probably out of date or simply incorrect) is that 'androidx.test:core:1.0.0' has integrated Robolectric, and you are using Robolectric without calling functions or importing directly.
You don't need to add the #RunWith annotation because in the Gradle file the tutorial has you add:
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
}
Despite this, I could not escape the exception I described by following the tutorial. So I had to include Robolectric directly:
testImplementation "org.robolectric:robolectric:4.3.1"
and this is my unit test class:
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertTrue;
#Config(maxSdk = 29)
#RunWith(RobolectricTestRunner.class)
public class UnitTestSample {
private static final String FAKE_STRING = "HELLO_WORLD";
#Test
public void clickingButton_shouldChangeResultsViewText() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
assertTrue(true);
}
}
Another thing I had to do was set the SDK to 29 with #Config, because Robolectric 4.3.1 doesn't support Android API level 30.

Related

How to start only SmallTest in Android Studio

I've started a new Project with testautomation. Some tests take some time These have been marked as #LargeTest. There are also some SmallTests (unit tests). The developers need to run the small tests more often.
I have searched through the web some time and saw how to run the tests by size via adb shell and gradle. But nowhere there was a solution directly in AndroidStudio
I have some Classes like this of this kind and some simple Core logic tests:
import androidx.test.filters.LargeTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
#LargeTest
public class ExampleActivityTest {
#Before
public void setUp() {
// Do setup
}
#After
public void tearDown() {
// reset changes
}
#Test
public void testSomeFancyStuff() {
// Test comes here
}
}
Some times only the Small and/or medium tests need to be executed. How to do this in Android Studio?

android unit test spannablestringbuilder

One of my classes uses SpannableStringBuilder and I need to write unit test code for it. This class is for general use and does not interact with any UI components. I considered it as a local unit test but Android Studio complains that 'unit test not mocked'. I've checked Android Mock and robolectric but I can't figure out which instrumentation I have to mock. I also tried putting the test under the AndroidTest directory and ran it as an instrumentation test but still got an error 'Class not found: "....." Empty test suit'.
The unit test code:
package xxxxxxx; // Sorry but I should keep it secret now.
import android.support.test.filters.SdkSuppress;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableString;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import xxxxxxx.HtmlParser;
import static org.junit.Assert.*;
#RunWith(AndroidJUnit4.class)
#SdkSuppress(minSdkVersion=16)
#SmallTest
public class HtmlParserTest {
#Test
public void TestGetHtml() {
// assertion goes here
}
#Test
public void TestGetPlainText() {
// assertion goes here
}
}
I don't know if it was because I did something wrong. Eventually I got it work and I'll post the solution here for your reference.
As metioned in my question, this unit test should run on an Android device or an emulator. So the first thing to do is moving it into the androidTest directory. Only in this way will Android Studio recognize it as an instrumentation test. Also remember to update your app's gradle.build as described here. But Android Studio (Mine is v2.1) won't allow you to run this test directly (as least I couldn't). You have to create a test suite which looks like:
package xxxxxxx.htmltestsuite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Suite.class)
#Suite.SuiteClasses({
HtmlParserTest.class
})
// place holder class
public class HtmlTestSuite {}
Today I meet a question that when I do junit test under test directory.When I create a SpannableStringBuilder,for example,
SpannableStringBuilder ssb = new SpannabelStringBuilder(str),
then the value of ssb is always "null". But I remove it under androidTest directory, and extends AndroidTestCase,it effect.

How to setup the environment for android instrumentation tests and make a sample Activity UI test?

Disclaimer
Recently I’ve need to start testing my Android Applications and I’ve found many problems during my way through a successful test. The answers for my troubles weren’t easy to find, therefore, I’ve decided to share what I’ve learned from the community to the community.
Can I answer my own question?
Yes! Stack Exchange has always explicitly encouraged users to answer their own questions. If you have a question that you already know the
answer to, and you would like to document that knowledge in public so
that others (including yourself) can find it later, it's perfectly
okay to ask and answer your own question on a Stack Exchange site.
https://stackoverflow.com/help/self-answer
https://meta.stackexchange.com/questions/17845/etiquette-for-answering-your-own-question
Is there a better answer?
Despite the specification provided by Google in Android documentation, several problems can arise for those who lack experience with Gradle and Android Studio and want to start developing instrumentation tests.
Understanding the subject
Before moving forwarding in this subject, take ten minutes and read this article. It will make you understand the basic concepts for testing your Application.
Understand the basic superclasses used in Android tests:
TestCase – Plain old JUnit test case. It can be extended to test utility classes that are not tied to the Android framework;
AndroidTestCase – It extends JUnit’s TestCase. It’s a lighter testing class compared to ActivityTestCase. It doesn’t need to launch
an activity to run it. Its getContext() method allows you to get an
injected context if you need one. Since you can get a context from
this class, you can inflate your UI objects to test their behaviors;
ActivityInstrumentationTestCase2 – It’s the newer version of ActivityInstrumentationTestCase. ActivityInstrumentationTestCase is
deprecated in Android SDK 1.5. It’s a heavier testing class compared
to AndroidTestCase. It provides UI and functional testing for a single
activity. You can get an injected activity that you are testing on by
calling its getActivity() method. The activity being tested is
launched and finished before and after each test;
ActivityUnitTestCase – It gives the tested activity an isolated environment. When using it to test an activity, the activity is not
attached to the system. This gives you more control over what kind of
environment that you want your activity to be tested in;
ApplicationTestCase – It provides testing for Application classes. It can be used to test the life cycle of an application;
InstrumentationTestRunner – The runner that runs the Android test cases. I just found this..Hope this helps for others...If u want
more details like when and how to use, see the APIDemos test
application in the samples directory within android SDK.
Credits for this answer, user Bharat Pawar
Using Android Test Framework;
01 - Setup your Gradle Module
In your app gradle module import the following libraries:
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.android.support:support-annotations:23.0.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support:support-annotations:24.0.0-alpha2'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2.1') {
exclude module: 'support-annotations'
exclude module: 'support-v4'
exclude module: 'support-v13'
exclude module: 'recyclerview-v7'
}
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}
02 - Write your activity test
This is a sample UI Test using JUnit4. In the examples found in the web, there will be people using JUnit3. This last one won’t make use of annotation but standard method names (starting with “test”), however, currently, Google tells you to use JUnit4 in your test
In your JUnit 4 test class, you can call out sections in your test
code for special processing by using the following annotations:
#Before: Use this annotation to specify a block of code that contains test setup operations. The test class invokes this code block
before each test. You can have multiple #Before methods but the order
in which the test class calls these methods is not guaranteed;
#After: This annotation specifies a block of code that contains test tear-down operations. The test class calls this code block after
every test method. You can define multiple #After operations in your
test code. Use this annotation to release any resources from memory;
#Test: Use this annotation to mark a test method. A single test class can contain multiple test methods, each prefixed with this
annotation;
#Rule: Rules allow you to flexibly add or redefine the behavior of each test method in a reusable way. In Android testing, use this
annotation together with one of the test rule classes that the Android
Testing Support Library provides, suh as ActivityTestRule or
ServiceTestRule;
#BeforeClass: Use this annotation to specify static methods for each test class to invoke only once. This testing step is useful for
expensive operations such as connecting to a database;
#AfterClass: Use this annotation to specify static methods for the test class to invoke only after all tests in the class have run.
This testing step is useful for releasing any resources allocated in
the #BeforeClass block;
#Test(timeout=): Some annotations support the ability to pass in elements for which you can set values. For example,
you can specify a timeout period for the test. If the test starts but
does not complete within the given timeout period, it automatically
fails. You must specify the timeout period in milliseconds, for
example: #Test(timeout=5000)
.
Source: https://developer.android.com/tools/testing/testing_android.html
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.widget.RecyclerView;
import android.test.ActivityInstrumentationTestCase2;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import br.com.hole19.marvel.R;
import br.com.hole19.marvel.ui.commons.util.ActivityTemplate;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
/**
* Created by Edgar on 04/05/2016.
*/
#RunWith(AndroidJUnit4.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestActivityHomepage extends ActivityInstrumentationTestC;se2 <ActivityHomepage> {
private static final String TAG = "TestActivityHomepage";
private ActivityHomepage mActivity;
public ActivityTestRule<ActivityHomepage> rule = new ActivityTestRule(ActivityHomepage.class, true, false);
public TestActivityHomepage() {
super(ActivityHomepage.class);
}
#Before
public void setUp() throws Exception {
super.setUp();
Intent intent = new Intent();
mActivity = rule.launchActivity(intent);
}
#Test
public void testPreconditions() {
assertNotNull(mActivity);
}
#Test
public void testLoadMoreCharacters () throws InterruptedException {
RecyclerView characters = (RecyclerView) this.mActivity.findViewById(R.id.characters);
int previousSize = characters.getAdapter().getItemCount();
characters.smoothScrollToPosition(previousSize);
Thread.sleep(2000);
int newSize = characters.getAdapter().getItemCount();
onView(withId(R.id.characters)).perform(RecyclerViewActions.scrollToPosition(previousSize));
assertNotSame(previousSize, newSize);
}
#Test
public void openActivityCharacter () {
onView(withId(R.id.characters))
.perform(
RecyclerViewActions
.actionOnItemAtPosition(0, click()));
assertFalse(mActivity.isRunning());
}
}
03 - Problems Investigation
During the process of creating the tests some problems may happen:
Problems 01: gradle finish with errors.
Occurs when: library conflict during gradle build;
Description: when importing test specific libraries it may get in conflict with Application already existent libraries;
Solution: exclude the conflicting module from the conflicting libraries;
Problems 02: instrumentation error fails with timeout.
Occurs when: Launch instrumentation test;
Description: Intrumentation test don’t support existance of progress bar in the activity being testes;
Solution: In the tested activity, make sure to set the progress bar visibility to GONE after finishing the loading;
More examples
If you want to check further examples of how to use test frameworks check out this link;

Android studio says "Empty Test Suite" for AndroidTestCase [duplicate]

This question already has answers here:
Why is the Android test runner reporting "Empty test suite"?
(31 answers)
Closed 6 years ago.
I have created an example test case that extends AndroidTestCase. When I run the test case,
it errors out by saying
Running tests
Test running startedTest running failed:
Instrumentation run failed due to 'java.lang.RuntimeException'
Empty test suite.
The test case
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import static org.junit.Assert.assertEquals;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.Exception;
import java.lang.Override;
public class DateFormatTest extends AndroidTestCase{
#Override
protected void setUp() throws Exception {
super.setUp();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
}
public DateFormatTest(){
super(DateFormatTest.class);
}
#SmallTest
public void testMultiply() {
assertEquals("10 x 5 must be 50", 50, 10*5);
}
}
Since nobody else mentions it: methods in AndroidTestCase subclasses need to be public and have names starting with "test"!
The OP and the answers all got this right but I missed it and got the exact same error.
I got this error when running tests from Android Studio. Turned out I had placed my test cases in the wrong folder. When running Gradle/Android Studio, all Android tests should be in the folder src/instrumentTest/java.
Edit: In Gradle plugin version 0.9 or later the correct name of the folder is androidTest. (http://tools.android.com/tech-docs/new-build-system/migrating_to_09)
I understand that this question is old, and the development tools have changed significantly since this question has been asked.
However, I had a similar issue (in AndroidStudio 2.1.1) and it turned out that it was just the error message that was quite misleading. For me it said:
Test running started
Test running failed: Instrumentation run failed due to 'java.lang.IllegalStateException'
Empty test suite.
(Note: The difference to the original question is in IllegalStateException vs. RuntimeException)
It turned out that this IllegalStateException was actually thrown in the initialization of the application context as can be seen by inspecting the logcat output. As a result, no test-methods were run, and Android Studio writes this somewhat misleading "Empty test suite" error.
I.e. "Empty test suite" can mean what it actually says (you don't have any test methods declared, e.g. because you forgot to annotate them with #Test), but it can also mean that something (like a runtime exception thrown in your application initialization code) prevents the test-runner from reaching any test methods (which seems to be your case).
Check adb-logcat and search for RuntimeExceptions. This will probably find you the root-cause of your problem.
I got this error because one of my test methods didn't include "throws exception" after the method signature. might help somebody
I got the same issue. I was having testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
in my defaultConfig {...}. I have just removed that line, and now it's working fine (The IDE is picking the right runner config on build time).
I hope this will help someone.
My problem was I had default constructor generated by android studio, looked like this
public class SomeTest extends ActivityUnitTestCase<ActivityYouWantToTest>{
public SomeTest(Class<NewsActivity> activityClass) {
super(activityClass);
}
}
and I had to change it to this to get rid of the problem
public class SomeTest extends ActivityUnitTestCase<ActivityYouWantToTest>{
public SomeTest() {
super(ActivityYouWantToTest.class);
}
}
We use the AndroidTestCase class to define that we are testing components which are specific to Android. The main benefit of AndroidTestCase is that it gives us access to the application's Resources such as strings and layouts, etc.
The AndroidTestCase does not require you to overwrite the default constructor as it does not provide a particular Activity as the Context, but rather provides you a general one so you can still call getContext().
In your example, you are not using any Android components, so for you, the following would make sense:
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
public class DateFormatTest2 extends TestCase {
#SmallTest
public void testMultiply() {
assertEquals("10 x 5 must be 50", 50, 10 * 5);
}
}
Notice the use of TestCase rather than AndroidTestCase.
For AndroidTestCase to be applicable, a test that requires resources would be necessary:
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
public class DateFormatTest extends AndroidTestCase {
#SmallTest
public void testAppTitle() {
assertEquals("MyApp", getContext().getResources().getString(R.string.app_name));
}
}
Here we use the AndroidTestCase because we need to access the application's resources.
This guide might help -
http://www.slideshare.net/tobiaspreuss/how-to-setup-unit-testing-in-android-studio
On the latest gradle (0.9+) the test should be under androidTest dir
Also in your gradle.build:
dependencies {
androidTestCompile 'junit:junit:4.+'
}
also add those under defaultConfig {
testPackageName "test.java.foo"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
}
Did you configure your testRunner in your gradleConfig?
We use different TestRunners for different tests (to speed things up. My config looks like this
android {
// Some stuff
defaultConfig {
// Some other stuff
//junit test
testInstrumentationRunner "de.qabel.qabelbox.QblJUnitRunner"
//ui tests
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
If i disable one of this lines the corresponding test will also report "Empty Test Suite".
I just started learning about testing Android applications and I've been struggling with the same problem. You need to provide default constructor for your test class, for example:
package nilzor.myapp.tests;
public class NilzorSomeTest extends ActivityUnitTestCase<ActivityYouWantToTest>{
public NilzorSomeTest(){
super(ActivityYouWantToTest.class);
}
#SmallTest
public void testBlah(){
assertEquals(1,1);
}
}
I already have a default constructor in my test case but still it was giving me error "Empty test suite" and was stuck at "Instantiating tests...".
Tried creating new workspace, resetting Android Studio, but that didn't work.
Finally, close Android SDK and emulator.
Go to your android-sdks/platform-tools.
Clear all Android temp files with these commands:
a. rm -rf ~/Library/Application Support/AndroidStudio
b. rm -rf ~/Library/Caches/AndroidStudio
c. rm -rf ~/Library/Application Support/AndroidStudio
d. rm -rf ~/Library/Preferences/AndroidStudio
Run:
./adb kill-server
./adb start-server
Start Android and run test case.

Randomise the order of instrumentation tests

I'm wondering if it's possible to randomise the order in which instrumentation tests are run, i.e. those extending ActivityInstrumentationTestCase2. I tried following this blog post, but I can't work out how to tell the testing framework that I wish to use my test runner.
The problem is that I can't use the #RunWith annotation, as these are (as I understand it) JUnit3 tests, rather than JUnit4.
It's quite possible that this is pointless, as they don't need to be randomised, but it would be nice to prove the tests' independence in this way.
Ideally I'd like to get it running first using the command line and the gradle wrapper.
Then, it would be nice to have it working via Android Studio, if possible.
[Edit]
I can see that when you do "Edit Configurations . . ." in AS, it's possible to specify your own runner there, via the "Specific instrumentation runner (optional)" box. Unfortunately if I do that, I get the following error:
Test running startedTest running failed: Unable to find instrumentation info for: ComponentInfo{<path_to_class_here>.RandomizingTestRunner}
Empty test suite.
And I can't work out why.
You could use the following randomized runner:
package com.example.test.runners;
import android.test.InstrumentationTestRunner;
import android.test.suitebuilder.TestSuiteBuilder;
import junit.framework.Test;
import junit.framework.TestSuite;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RandomizedInstrumentationTestRunner extends InstrumentationTestRunner {
#Override
public TestSuite getTestSuite() {
return buildTestSuite();
}
private TestSuite buildTestSuite() {
TestSuiteBuilder builder = new TestSuiteBuilder(getClass().getName(), getTargetContext().getClassLoader());
builder.includePackages("");
List<Test> tests = new ArrayList<Test>();
addTestsFromSuite(builder.build(), tests);
Collections.shuffle(tests);
TestSuite randomizedSuite = new TestSuite();
for (Test one : tests) {
randomizedSuite.addTest(one);
}
return randomizedSuite;
}
private void addTestsFromSuite(TestSuite suite, List<Test> out) {
List<Test> tests = Collections.list(suite.tests());
for (Test one : tests) {
if (one instanceof TestSuite) {
addTestsFromSuite((TestSuite) one, out);
}
else{
out.add(one);
}
}
}
}
and don't forget to set the runner in your build.gradle file:
android {
defaultConfig {
testInstrumentationRunner "com.example.test.runners.RandomizedInstrumentationTestRunner"
minSdkVersion 8
}
....
}
Finally run the following twice to verify the random order of execution:
./gradlew connectedCheck --info

Categories

Resources