How to fill-in EditText field in Android tests - android

Does anybody have any experience with creating tests for an android project? I started setting up my testing project and noticed that there is not much documentation at all. I am trying to setup a test to test a username and password field. I have it working without filling in the inputs username or password. But now when I try to set those up I keep getting java.lang.NullPointerException but I don't see why or how. Here is a sample of code that I am working on.
public class GasTrackerTab1Test extends ActivityInstrumentationTestCase2<GasTrackerTab1> {
private Activity mActivity; // MyActivity is the class name of the app under test
private EditText username;
private EditText password;
private Button loginButton;
#SuppressWarnings("deprecation")
public GasTrackerTab1Test() {
super("com.wallproductions.gas.tracker", GasTrackerTab1.class);
}
#Override
protected void setUp() throws Exception {
/*
* Call the super constructor (required by JUnit)
*/
super.setUp();
/*
* prepare to send key events to the app under test by turning off touch mode.
* Must be done before the first call to getActivity()
*/
setActivityInitialTouchMode(false);
/*
* Start the app under test by starting its main activity. The test runner already knows
* which activity this is from the call to the super constructor, as mentioned
* previously. The tests can now use instrumentation to directly access the main
* activity through mActivity.
*/
mActivity = getActivity();
username = (EditText)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.login_user_name);
password = (EditText)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.login_password);
loginButton = (Button)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.mainloginbtn);
} // end of setUp() method definition
/*
* Tests the initial values of key objects in the app under test, to ensure the initial
* conditions make sense. If one of these is not initialized correctly, then subsequent
* tests are suspect and should be ignored.
*/
public void testPreconditions() {
assertNotNull(username);
assertNotNull(password);
}
public void testInvalidUserNamePassword() {
mActivity.runOnUiThread(
new Runnable() {
public void run() {
username.setFocus();
username.setText("tester");
password.setFocus();
password.setText("test1234");
loginButton.performClick();
}
}
);
}
}
The question is there any good documentation to look at to figure this out? Also how do you fill in a EditText box with some text and then verify through an alert that the correct response is given.

This worked for me:
onView(withId(R.id.text_edit_id)).perform(typeText(someString));

I believe I have found my answer at
http://www.java2s.com/Open-Source/Android/android-core/platform-tools-tradefederation/com/android/tradefed/uitestapp/EditBoxActivityTest.java.htm
Basically I needed to add in #UiThreadTest above my method and then using setText works. Not using the Runable class.
public class GasTrackerTab1Test extends ActivityInstrumentationTestCase2<GasTrackerTab1> {
private Activity mActivity; // MyActivity is the class name of the app under test
private EditText username;
private EditText password;
private Button loginButton;
#SuppressWarnings("deprecation")
public GasTrackerTab1Test() {
super("com.wallproductions.gas.tracker", GasTrackerTab1.class);
}
#Override
protected void setUp() throws Exception {
/*
* Call the super constructor (required by JUnit)
*/
super.setUp();
/*
* prepare to send key events to the app under test by turning off touch mode.
* Must be done before the first call to getActivity()
*/
setActivityInitialTouchMode(false);
/*
* Start the app under test by starting its main activity. The test runner already knows
* which activity this is from the call to the super constructor, as mentioned
* previously. The tests can now use instrumentation to directly access the main
* activity through mActivity.
*/
mActivity = getActivity();
username = (EditText)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.login_user_name);
password = (EditText)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.login_password);
loginButton = (Button)mActivity.findViewById(com.wallproductions.gas.tracker.R.id.mainloginbtn);
} // end of setUp() method definition
/*
* Tests the initial values of key objects in the app under test, to ensure the initial
* conditions make sense. If one of these is not initialized correctly, then subsequent
* tests are suspect and should be ignored.
*/
public void testPreconditions() {
assertNotNull(username);
assertNotNull(password);
}
/**
* Test that the edit box on {#link EditBoxActivity} can focused.
*/
#UiThreadTest
public void testUsernameTextFocus() {
assertNotNull(username);
assertTrue(username.requestFocus());
assertTrue(username.hasFocus());
}
#UiThreadTest
public void testPasswordTextFocus() {
assertNotNull(password);
assertTrue(password.requestFocus());
assertTrue(password.hasFocus());
}
#UiThreadTest
public void testInvalidUserNamePassword() {
username.requestFocus();
username.setText("testing");
password.requestFocus();
password.setText("whatever");
loginButton.callOnClick();
}
}

Related

How to test element of custom sdk android unit test

I've started learning android unit tests, but it looks very hard to find some good guides or information. Every example have a stupid example about 2+2 = 4
Say I write a little SDK which has few functions
MySdk.Init(Context context)
MySdk.CallTask()
I create an androidTest file
How should I call my SDK functions to check how they work? Somewhere required parameters like int/string/context. I just really don't understand, please help me.
This is what I've tried
public class AndroidTest {
private Activity context;
//default test
#Test
public void addition_correct() throws Exception {
assertEquals(4, 2 + 2);
}
#Test
public void checkContext() {
context = getActivity();
assertNotNull(context);
}
#Test
public void testInitPhase() {
MySdk.Init(context, new SdkInitializationListener() {
#Override
public void onInitializationSuccessful(String adv_id) {
assert (adv_id != null);
}
#Override
public void onInitializationError() {
}
});
}
}
For context i was tried context = new mockContext();. It's passed as context = null and my SDK failed with initialization.
Unit tests are mainly about testing an individual class in isolation, so that you can check if individual public methods of a class behave as you intend them to, and continue to do so if you change that class' code in the future. Let's say you have a class like this:
public class UtilityFunctions {
public int double(int value) {
return value * 2;
}
public String mirror(String value) {
if (value == null) return "";
return value + new StringBuilder(value).reverse().toString();
}
}
You want to test these two methods with:
valid input values, and check the output is as expected
invalid values, and check that errors are handled accordingly (and the correct exceptions thrown if necessary)
So a test class for the above class may look like this
#RunWith(JUnit4.class)
public class UtilityFunctionsTest {
private UtilityFunctions utility;
#Before
public void setUp() {
// Initialises any conditions before each test
utility = new UtilityFunctions();
}
#Test
public void testDoubleFunction() {
assertEquals(2, utility.double(1));
assertEquals(8, utility.double(4));
assertEquals(-12, utility.double(-6));
assertEquals(0, utility.double(0));
}
#Test
public void testMirror() {
assertEquals("", utility.mirror(null));
assertEquals("", utility.mirror(""));
assertEquals("aa", utility.mirror("a"));
assertEquals("MirrorrorriM", utility.mirror("Mirror"));
}
}
These standard Java unit tests are run from the test directory. However, you'll need to run tests in the androidTest directory whenever you're using Android-specific classes such as Context. If you're creating a MockContext, you're simply creating an empty Context whose methods don't do anything.
Without me knowing anything about what your MySDK does, I think you may need to pass a fully-functioning Context into your class for your tests. The Android JUnit runner does provide this with InstrumentationRegistry.getTargetContext(), so for your example, you may need to add this #Before method:
#Before
public void setUp() {
context = InstrumentationRegistry.getTargetContext();
}
You'll also need to remove the context = getActivity(); line from your first test.

Android Espresso: Wait for Activity to finish/start

Is there a canonical solution using Espresso to wait for a specific Activity to finish or start?
I have a SplashActivity that appears for a few seconds, then a MainActivity. I want Espresso to interact with the MainActivity, not the SplashActivity, but I can't seem to find any information about waiting for such a condition.
The closest thing I can find is a mention of idle resources but its not clear to me how I would use that here to wait for the Activity.
I guess your splash activity is performing some initialization.
If this is the case, my suggestion is to define some sort of listener pattern in order to be able to get a callback when the initialization is done. Then, you can make Espresso wait for the initialization with an IdlingResource.
NB: The following is NOT complete code, but it is meant to give you a hint in how to do so:
public class SplashIdlingResource implements IdlingResource, YourApplicationInitListener {
// volatile because can be set by a different
// thread than the test runner: the one calling back
private volatile boolean mIsInitialized;
private ResourceCallback mCallback;
public SplashIdlingResource() {
YourApplication application = // retrieve your Application object
mIsInitialized = application.isInitialized();
if (!mIsInitialized) {
application.addInitListener(this);
}
}
#Override
public String getName() {
return SplashIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return mIsInitialized;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
#Override
public void onApplicationInitCompleted() {
m_isInitialized = true;
if (m_callback != null) {
m_callback.onTransitionToIdle();
}
}
}
Where onApplicationInitCompleted() is the callback you defined and which must be called when the Splash Activity, and so the initialization, is done.
Finally, register this new IdlingResource with Espresso by calling Espresso.registerIdlingResource in test setup.

Consecutive Android Junit tests do not reflect the real data in the underlying database

Additional Information:
To clarify, the app under test uses a ContentProvider to access the database.
Edit:
If anyone is willing and able to help me debug this. The full project is available here. In the issue107-contentprovider branch, BaseballCardListAddCardsTest.
Question:
When I run two of my Android JUnit tests separately, they pass just fine. However, when I run them together, the first one passes and the second one fails. The problem appears to be that the first test run adds a row to the underlying database. tearDown() correctly deletes the database, but the second test still starts with the dirty data displayed in the ListView although the database does not contain the extra row. (I confirmed this using adb shell.) Does anyone have any ideas how I can fix this problem?
The Activity class being tested can be found here.
Here is my test code:
/**
* Tests for the {#link BaseballCardList} activity when the database contains
* data.
*/
public class BaseballCardListWithDataTest extends
ActivityInstrumentationTestCase2<BaseballCardList> {
/**
* Create instrumented test cases for {#link BaseballCardList}.
*/
public BaseballCardListWithDataTest() {
super(BaseballCardList.class);
}
/**
* Set up test fixture. This consists of an instance of the
* {#link BaseballCardList} activity, its {#link ListView}, and a populated
* database.
*
* #throws Exception
* If an error occurs while chaining to the super class.
*/
#Override
public void setUp() throws Exception {
super.setUp();
this.inst = this.getInstrumentation();
// Create the database and populate table with test data
InputStream cardInputStream = this.inst.getContext().getAssets()
.open(BBCTTestUtil.CARD_DATA);
BaseballCardCsvFileReader cardInput = new BaseballCardCsvFileReader(
cardInputStream, true);
this.allCards = cardInput.getAllBaseballCards();
cardInput.close();
this.dbUtil = new DatabaseUtil(this.inst.getTargetContext());
this.dbUtil.populateTable(this.allCards);
// Start Activity
this.activity = this.getActivity();
this.listView = (ListView) this.activity
.findViewById(android.R.id.list);
this.newCard = new BaseballCard("Code Guru Apps", 1993, 1, 50000, 1,
"Code Guru", "Code Guru Devs", "Catcher");
}
/**
* Tear down the test fixture by calling {#link Activity#finish()} and
* deleting the database.
*
* #throws Exception
* If an error occurs while chaining to the super class.
*/
#Override
public void tearDown() throws Exception {
this.dbUtil.deleteDatabase();
super.tearDown();
}
/**
* Check preconditions which must hold to guarantee the validity of all
* other tests. Assert that the {#link Activity} to test and its
* {#link ListView} are not <code>null</code>, that the {#link ListView}
* contains the expected data, and that the database was created with the
* correct table and populated with the correct data.
*/
public void testPreConditions() {
Assert.assertNotNull(this.activity);
BBCTTestUtil.assertDatabaseCreated(this.inst.getTargetContext());
Assert.assertTrue(this.dbUtil.containsAllBaseballCards(this.allCards));
Assert.assertNotNull(this.listView);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.allCards,
this.listView);
}
/**
* Test that the {#link ListView} is updated when the user adds a new card
* which matches the current filter.
*
* #throws Throwable
* If an error occurs while the portion of the test on the UI
* thread runs.
*/
public void testAddCardMatchingCurrentFilter() throws Throwable {
this.testYearFilter();
Activity cardDetails = BBCTTestUtil.testMenuItem(this.inst,
this.activity, R.id.add_menu, BaseballCardDetails.class);
BBCTTestUtil.addCard(this, cardDetails, this.newCard);
BBCTTestUtil.clickCardDetailsDone(this, cardDetails);
this.expectedCards.add(this.newCard);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.expectedCards,
this.listView);
}
/**
* Test that the {#link ListView} is updated when the user adds a new card
* after an active filter was cleared.
*
* #throws Throwable
* If an error occurs while the portion of the test on the UI
* thread runs.
*/
public void testAddCardAfterClearFilter() throws Throwable {
this.testClearFilter();
Activity cardDetails = BBCTTestUtil.testMenuItem(this.inst,
this.activity, R.id.add_menu, BaseballCardDetails.class);
BBCTTestUtil.addCard(this, cardDetails, this.newCard);
BBCTTestUtil.clickCardDetailsDone(this, cardDetails);
this.allCards.add(this.newCard);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.allCards,
this.listView);
}
private List<BaseballCard> allCards;
private List<BaseballCard> expectedCards;
private Instrumentation inst = null;
private Activity activity = null;
private DatabaseUtil dbUtil = null;
private ListView listView = null;
private BaseballCard newCard = null;
private static final int TIME_OUT = 5 * 1000; // 5 seconds
private static final String TAG = BaseballCardListWithDataTest.class
.getName();
}
It appears that a ContentProvider's lifecycle is tied to that of an Application not of the Activity that acesses it. Also, from what I can tell, ActivityInstrumentationTestCase2 creates a single Application for all the tests; only the Activity is destroyed and restarted for each test. This means that the each test will share the same ContentProvider. This means that the database file is opened with the first access by the ContentProvider and closed only after all test methods in the ActivityInstrumentationTestCase2 have finished. Since the database file remains open between test cases, the data can be accessed even after the file is deleted from the underlying file system. My solution was to delete the rows of the database individually rather than deleting the entire database.

How to determine if Android Application is started with JUnit testing instrumentation?

I need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.

Testing non-activity classes in Android

I know how to test Activity classes with JUnit 4 in Android but I am unable to understand how to test non-activity classes (which don't extends Activity, ListActivity, or some other Activity class, but uses some Android APIs). Please help me in this regard.
To test non activity classes:
create a test project
create a test case
run as Android JUnit Test
public class MyClassTests extends TestCase {
/**
* #param name
*/
public myClassTests(String name) {
super(name);
}
/* (non-Javadoc)
* #see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
}
/* (non-Javadoc)
* #see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* Test something
*/
public final void testSomething() {
fail("Not implemented yet");
}
}
The Android SDK includes JUnit. In fact, the Android test classes such as AndroidTestCase and InstrumentationTestCase inherit from junit.framework.TestCase. This means that you can use a standard JUnit test case to test a non-Activity class and include it in Android Projects.
For example, you can create an Android Project with a simple class to test:
public class MyClass {
public static int getOne() {
return 1;
}
}
and an Android Test Project with a standard JUnit test to test this class:
public class TestMyClass extends TestCase {
public void testMyClass() {
assertEquals(1, MyClass.getOne());
}
}
and run this on an Android device or on the Android emulator.
More information after seeing clarification of question in the comments:
AndroidTestCase or other Android test classes can be used to test non-Activity classes which need access to the rest of the Android framework (with a dummy activity provided in the setUp() if necessary). These give you access to a Context if you need to, for example, bind to a service.

Categories

Resources