android unit test spannablestringbuilder - android

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.

Related

Kotlin Test suite: An annotation argument must be a compile-time constant

I created a test suite class that I created to run my Espresso instrumented tests. For some reason, when I try to run it, the build fails with a load of build errors, including "Unresolved reference" for each of the modules and tests, and "An annotation argument must be a compile-time constant". Is it an issue with the tests living in different directories?
package org.x.android.group_1
import org.x.android.group_1.module_x.TestA
import org.x.android.group_1.module_x.TestB
import org.x.android.group_1.module_y.TestC
import org.x.android.group_1.module_y.TestD
import org.junit.runner.RunWith
import org.junit.runners.Suite
#RunWith(Suite::class)
#Suite.SuiteClasses(
TestA::class,
TestB::class,
TestC::class,
TestD::class
)
class TestSuiteGroup1
edit: the tests I'm passing in are written in Java, could that be an issue?
I know this was posted a while ago, but I came across this same issue.
Turned out that I needed to put my tests in the
androidTest/java/com/example/appname folder instead of the
test/java/com/example/appname folder
Once I moved everything it worked great!

Following Android docs on unit testing and failing

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.

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