Today, we're going to add some functionality to test the entry of invalid values. Let's get busy.
First, I'll make a copy of an existing test using ActivityInstrumentationTestCase2. I know it's kind of slow, but I'm going to be accessing resources and I think I need it for that. Actually, I'm not sure. Let's try accessing a resource from a test that uses ActivityUnitTestCase. I'm having trouble with deciding. I think I just need to play it safe and follow along with the examples from Google. What were those? Oh, yeah, it was Spinner. Let's take a look at some of its code:
@Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mActivity = getActivity();
mSpinner = (Spinner) mActivity
.findViewById(com.android.example.spinner.R.id.Spinner01);
mPlanetData = mSpinner.getAdapter();
} // end of setUp() method definition
In the setup, it's grabbing the control it's intending to test, plus it's adapter.
// this is run only once at the start of the app,
// to make sure the project is initialized correctly
public void testPreConditions() {
assertTrue(mSpinner.getOnItemSelectedListener() != null);
assertTrue(mPlanetData != null);
assertEquals(mPlanetData.getCount(), ADAPTER_COUNT);
} // end of testPreConditions() method definition
Here it's checking to make sure everything's been initialized.
public void testSpinnerUI() {
mActivity.runOnUiThread(
new Runnable() {
public void run() {
mSpinner.requestFocus();
mSpinner.setSelection(INITIAL_POSITION);
} // end of run() method definition
} // end of anonymous Runnable object instantiation
); // end of invocation of runOnUiThread
This looks like it creates a new thread. It sets the focus on the control and sets the selection on it. I remember reading something about how you need to test UI on the UT thread - but it was done as an annotation.
Here's the rest of the method:
this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
for (int i = 1; i <= TEST_POSITION; i++) {
this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
} // end of for loop
this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
mPos = mSpinner.getSelectedItemPosition();
mSelection = (String)mSpinner.getItemAtPosition(mPos);
TextView resultView =
(TextView) mActivity.findViewById(
com.android.example.spinner.R.id.SpinnerResult
);
String resultText = (String) resultView.getText();
assertEquals(resultText,mSelection);
} // end of testSpinnerUI() method definition
Ok, let's model test activity based on spinner code. I also found another link,
http://mobile.tutsplus.com/tutorials/android/android-sdk-junit-testing/
which looks like a nice simple example.
We'll try something like this:
public void testSendKeys() {
mActivity.runOnUiThread(
new Runnable() {
public void run() {
mLevel.requestFocus();
//mLevel.setSelection(INITIAL_POSITION);
} // end of run() method definition
} // end of anonymous Runnable object instantiation
); // end of invocation of runOnUiThread
/// now on level
sendKeys(NUMBER_5);
// now on start quiz number
sendKeys(NUMBER_1);
// now on end quiz number
sendKeys(NUMBER_1000);
// save
sendKeys("ENTER");
Assert.assertEquals(true, true);
}
Ok, it crashed on an index out of bounds. I noticed it looked like it wasn't clearing the fields before it populated them. It was too fast. Let's unplug an run it on the emulator, which is much slower. Woah - way too slow. Back to the device.
Ok, after a little experimentation, this sequence gives me a passing test:
// / now on level
sendKeys(KeyEvent.KEYCODE_BACK);
sendKeys(KeyEvent.KEYCODE_DEL);
sendKeys(NUMBER_5);
// now on start quiz number
sendKeys(KeyEvent.KEYCODE_BACK);
sendKeys(KeyEvent.KEYCODE_DEL);
sendKeys(NUMBER_1);
// now on end quiz numbe
for (int i = 0; i < 5; i++) {
sendKeys(KeyEvent.KEYCODE_BACK);
}
for (int i = 0; i < 5; i++) {
sendKeys(KeyEvent.KEYCODE_DEL);
}
sendKeys(NUMBER_1000);
// save
sendKeys("ENTER");
Assert.assertEquals(true, true);
Now, let's see what happens when we put a crazy value in there somewhere.
sendKeys(NUMBER_9999);
9999 is a larger number then the available vocabulary limit.
I'm just having a lot of difficulty figuring out what's going on when the test runs; on the device, it goes by too fast, and sometimes the screen size changes. Also, it's not at clear it's entering the digits I want. Plus, I know it should be crashing, and it isn't. The emulator is incredibly slow.
So, I'm going to go to plan B and try Robotium. This is a frustrating thing. A morning's worth of work, pretty much trashed. This is the downside of TDD on Android. The tools are hard to work with.
Let's see, is there a tutorial or something?
Yeah. Ok, a youtube video. Ok, download the sample project. Ok, it needs notepad list. Ok, got that. Run the test. Ok, seems to work.
Let's make a copy of that project. Ok, don't forget to change the target package name in the manifest; and the package name. Add the target package to the projects tab in settings.
ok, let's try something like this:
solo.clickOnButton(0);
Wow, very cool. Not only did it press the correct button, it went to the next activity screen, without my having to specifically load it. Multi activities, something I hadn't even gotten close to with the Android testing framework.
I like the Robotium api - all the events are easy to pick out. It supports things like clear text.
Lets see if we can enter some text...
solo.clickOnButton(0);
solo.clearEditText(0);
solo.enterText(0, "17");
Ok, how to I save it?
solo.clickOnButton(0);
solo.clearEditText(0);
solo.enterText(0, "17");
solo.clickOnButton(0);
Index out of bounds - perfect! Amazing - this is so much easier than Android testing framework.
Ok, let's fix up the code that causes this problem. Suddenly, I'm back into production code. Actually - this should work for the lifecycle checks I had to comment out, which I went to such pains to create.
Let's fix index out of bounds.
Ouch. Again, adding test code to return a value is just a bad idea. Especially a hardcoded one. It was ok when I was building the function, but anything hardcoded has to be cleaned out pretty quickly. Let's comment out that code.
Pass.
The problem is, I now have tests to run from two separate projects. I wonder how many of the android junit test I could bring into this one. Either that, or just test everything functionally. I think there's some I couldn't really do that for, that really do test actual methods.
Well, that's good for now. Let's pick up some more of the great Robotium in the next post.
No comments:
Post a Comment