How to programatically test UI action/activities without repeating test code? - android

I am very new to TDD and doing TFD in particular. I have not written any codes yet and I wanted to write a test first before develop everything in conjunction with TDD. I want your insight. It seems I am copy pasting my test code. I have made my 'pseudo' user story for me to practice. I am going to paraphrase as this is an actual personal project so please bear with me.
"User can search for a tag"
I have a UI that allows to add and to search for a tag. I made it a little bit conservative by using minimal design. I have a button which toggles between add/search string. I have a CardView to represent this, that CardView is part of the list (its like Facebook). Now I wanted to test that when the user press the button, the content on that card will change to search mode. I pretty much have an idea on how to do this but copy pasting my test code per each test is kind of bothering me.
Here is my test:
public class TagListActivityTest
{
#Test
public void shouldHaveAddTagCard()
{
// User tapped to expand the card
onView(withId(R.id.edittext_description_minimized))
.perform(click());
// User sees the expanded card
onView(withId(R.id.linearlayout_add_note_maximize))
.check(matches(isDisplayed()));
// User sees the expanded card's quick action buttons
onView(withId(R.id.relativelayout_quick_action_button))
.check(matches(isDisplayed()));
// User clicks the add tag button
onView(withId(R.id.imagebutton_tag))
.perform(click());
// User sees the tag list
onView(withId(R.id.coordinatorlayout_tag_list))
.check(matches(isDisplayed()));
// User sees the add tag card
onView(withId(R.id.cardview_add_tag))
.check(matches(isDisplayed()));
}
#Test
public void shouldToggleToSearch()
{
// I am going to do the exact same thing as shouldHaveAddTagCard
// starting from my parent activity until here...
onView(withId(R.id.edittext_description_minimized))
.perform(click());
onView(withId(R.id.linearlayout_add_note_maximize))
.check(matches(isDisplayed()));
onView(withId(R.id.relativelayout_quick_action_button))
.check(matches(isDisplayed()));
onView(withId(R.id.imagebutton_tag))
.perform(click());
onView(withId(R.id.coordinatorlayout_tag_list))
.check(matches(isDisplayed()));
}
}
The TagListActivity is originating from a parent activity. There is some bunch of things you have to do before you can go through the TagListActivity and I already have written test for it. So when I test TagListActivity I have to go first in application's homescreen and navigate from there as you can see from my test procedure shouldHaveAddTagCard. This is my problem, I have to write that procedure over and over again. So when I wanted to test shouldToggleSearch I have to go from the parent activity and write those tests again until I reached TagListActivity. I think I am doing something wrong.
So my question is:
How can I organize this when there is a known user action procedure.
I have written test per procedure to make sure it does what I wanted
to be.
no. 1 makes me feel there is something wrong in what I am doing. I am testing per action (ie user adds tag, user search tag, user deletes tag). So the pre-procedure
I did before user can add tags is the same as user can search tag and I have
to copy paste those pre-procedure before I can actually test.
Also, it seems that I cannot call a test method from a test method as discussed here. I am thinking of reusing test code but it is not advisable.
Am doing things correctly? Any thoughts?

To be honest your tests look very good if this is your first time doing TDD.
Reducing duplication
You can use the #Before annotation to execute some code before each test. In your case, it might look something like this:
// this method will be executed before each test
#Before
public void clickOnEditTextDescription() {
onView(withId(R.id.edittext_description_minimized))
.perform(click());
// put as much set up code in here as you need
}
Bear in mind that, in general, you should not make any assertions in the #Before method. It is for set up code only.
But is it always a good thing?
#Before methods are great, however, remember that copying and pasting test code is not always a bad thing. It's a different balance to production code. In production code, you want no duplication because any given piece of business logic should only exist in one place. In test code however, each test needs to be completely independent from all the other tests. If got rid of all the duplication in your test code, then it would be very difficult to change the shared code without breaking all your tests. Furthermore, your tests would be harder to read because you would have to keep referring to the shared code.
I recommend that you do some research on DAMP (descriptive and meaningful phrases) vs DRY (don't repeat yourself). DAMP is more relevant for unit tests, and allows you to repeat yourself sometimes. DRY is more relevant for production code. The following answer is great at explaining this:
https://stackoverflow.com/a/11837973/6816469

Related

Understanding results of Android Espresso instrumentation test

I've created a simple test using Espresso in Android Studio.
The test runs to completion and gives a success message.
However, I'm testing to see if two Button views are visible or not.
I expect to see that one button is visible and the other is not
The test result does not mention this at all. The goal of this test is to run it via Firebase Test Lab on various virtual and real devices and read the results of the test!
Here is the simple test on Android Studio:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MyGraphingTest {
#Rule
public ActivityScenarioRule<MainActivity> activityRule
= new ActivityScenarioRule<>(MainActivity.class);
#Test
public void MyTestMethod(){
//some test instructions that will lead me to where the two Button views are...
//more test instructions that will hide one Button view from screen, while the other Button view remains visible.
onView(withId(R.id.recenter)).check(matches(isDisplayed()));
onView(withId(R.id.buttonGraph2)).check(matches(isDisplayed()));
}
}
see the attached picture to see the test results!
All checks "returned" true, so your test is passed. Otherwise, test will fall and you will see the reason of the failure. If you want to see additional information about your checks, you should add some logs mannualy.
However, I'm testing to see if two Button views are visible or not. I expect to see that one button is visible and the other is not
Your test, as you posted it, is looking for both views to be displayed. Keep in mind that visible means View.VISIBILITY == VISIBLE. Displayed means you could actually see it in the current screen. Thus a visible view may not actually be displayed if it's off screen or has a zero width or height.
If you actually want to test visibility, you should use withEffectiveVisibility instead of isDisplayed.
The test result does not mention this at all.
You're testing that both views are displayed and the test passes. What do you expect the test to "mention"? Since your test passed, all the assumptions that are in the test held true and the test result has nothing more to tell you.

How to use method By.clazz from UIAutomator test framework

I'm using UIAutomator test framework for long tests (concerning to my acceptance test). And I need to wait until some activity is started.
I decided to use By.clazz (android.support.test.uiautomator package) methods to find activity object. I expected that something like
uiDevice.wait(Until.findObject(By.clazz(SomeActivity.class)), 30000);
will work. But it doesn't. I suppose that object of my activity cannot be found. I tried to use other By.clazz methods with different params but without success.
So, my code is pretty simple:
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
/*.... do something...
like click on buttons which will open some activities...
*/
//does not work, time value just for sample
uiDevice.wait(Until.findObject(By.clazz(SomeActivity.class)), 30000);
I found workaround solution with using By.res, like
uiDevice.wait(Until.findObject(By.res(BASIC_PACKAGE, "someMainIdInSomeFragment")), 30000);
But I have very complicated structure of the app with base activities and so on. I often have the same layout for different activities with load different fragments. So I need to know that we started exactly SomeActivity ,regardless of loaded fragments.
So, the questions are:
Is it possible to use By.clazz for Activity to find its object?
Is there some another way to find activity object with UIAutomator?
Did I do everything right? Or maybe there are some mistakes? Is it possÑ–ble to do with UiAutomator?
Thanks!
Using class with UiObject2
Find the EditText, make sure to click on it (legacySetText would do it implicitly), and set your text.
val input = By.clazz(EditText::class.java.canonicalName)
device.wait(Until.findObject(input), TIMEOUT).apply {
click()
text = "your_text"
}
Yes, could be done through the id.
// android:id="#+id/widget_id"
By.res(applicationId, "widget_id")
Your syntax seems good to me. Just make sure no spinner (or any other widget) is blocking your view during the click attempt.

Avoid a block of code to run during unit test using mokito

I am using Mokitio in android to run unit test cases.
.
What i am trying to do: There is a block of code in onCreate event
of the activity
I am trying not to run this block of code during Running Unit test
cases and run it during app regularly.
Is it possible to do something like that using mokito because mokito synchronizes for activity life cycle
The proper solution here is to change your design a bit. You should not think in terms of code blocks, but in terms of functionality.
The way of preventing that some x lines of code are run in a certain environment, but are not in some other context ... is by using proper OO means.
Meaning: first create an interface that describes the functionality of those lines of code we are talking about:
public interface DoTheFoo {
public void foo(Bar bar);
}
Then you create a "production" implementation DoTheFooImpl of that interface (which as a side effect: you might be able to write proper unit tests for as well).
Finally: within your class that needs that functionality, use dependency injection to acquire an object providing the DoTheFoo interface. In your production environment, that would be a DoTheFooImpl object; but for your unit testing, you would simply create an mock for it - configured to do nothing upen calls to foo().
Of course that sounds like a bit of work; but the point is: currently, your design is somehow deficient. And instead of trying to go for dirty hacks/workarounds, consider looking at your design to identify a more elegant way to resolve your problem.

Android: Robotium vs android test framework

Everyone using Robotium for GUI testing.
Can you tell me what Android native test framework cannot do that Robotium can do?
As I know Robotium can be used as black box testing, so I do not need to know about application resources.
What else?
Robotium
pros:
support hybrid applications with webViews.
you can bound your test to unique IDs of tested application whenever with UIatomator to write complicated test cases will need great amount of work from you
in UIatomator is used UiAutomatorTestCase Instrumentation which doesn't give you possibilities to call current activity and check that loaded appropriate one, you can't call connectivity- or audio- managers for wi-fi or sound tests. In Robotium you can easily call such gems and that is greatly improve efficiency of your tests
Open source project, so you can modify tool to your needs
cons:
No possibility to tests multi-process application where android:process tag used for different activities.
example code:
UIautomator
pros:
Test case is independent from the process at which tested application works. So it can be used in places where are used additional libraries or activity is run in other process also useful if for test needed switching between several applications.
cons:
Works only on Android version 4.1 or more!
You can't use source IDs when you get ui-object instances. That mean if an application structure changed on one layout you need to refactor your test (this problem can be solved by using tags for each ui-element)
You can't get current activity or Instrumentation. That mean you are limited in development of your tests and not used many of android's api methods for your tests.
Hard to debug, you need to have script for buiding and starting your test fast and see output
hierarchy viewer:
I think both these tools are good ones, definitely possibility to switch between applications during test is awesome. You should select tool depending from your needs. I suggest Robotium for tests where you don't need to switch between applications because it have simple methods and opportunity to use Android API for writing flexible and smart tests in short code which can cover even testing of a web page inside webview and milti-process applications are very unusual.
The difference between Robotium and the native tools is the fact that with Robotium is really simple to write a test. It's basically point and click, from the Solo instance object.
Here you can download the JAR files and an example project to test it by yourself.
UPDATE
As an example, I'm testing an Activity with some Edit Text, a spinner and a pop-up dialog that shows up when I click a spinner option. Note here that with other methods, filling the fields of the pop up dialog is a real pain.
Here is how to declare the test class and Robotium's initialization
import com.jayway.android.robotium.solo.Solo; //Using Robotium
//Robotium uses ActivityInstrumentationTestCase2.
//Note here the use of the template
public class AddNewQuestionTests extends
ActivityInstrumentationTestCase2<AddNewQuestion> {
public AddNewQuestionTests(Class<AddNewQuestion> name) {
super(name);
}
public AddNewQuestionTests() {
super(AddNewQuestion.class);
}
private Solo solo;
protected void setUp() throws Exception {
super.setUp();
//Initialize Solo with the instrumentation and the activity under test
solo = new Solo(getInstrumentation(), getActivity());
}
And here is the test method:
public void testHappyPathAddScaleQuestion() {
// Type question title
solo.clickOnEditText(0); //find the EditText, and click it
solo.enterText((EditText) getActivity().findViewById(
//find the EditText, and put some string
R.id.activity_add_new_question_editText_question_title),
"Question title scale ");
// Type question description
solo.clickOnEditText(1);
solo.enterText((EditText) getActivity().findViewById(
R.id.activity_add_new_question_editText_question_description),
"Question description scale");
// Type the question
solo.clickOnEditText(2);
solo.enterText((EditText) getActivity().findViewById(
R.id.activity_add_new_question_editText_question),
"Question scale");
// Click the spinner and then the "Scale" question type
//Press an spinner option
solo.pressSpinnerItem(0, 4);
//Wait for the popup dialog title to show up. When robotium reads it, continue working solo.waitForText(getActivity().getResources().getString(R.string.activity_add_new_question_scale_selection_dialog_message));
// Type minimum and maximum ranges
solo.clickOnEditText(0);
solo.searchText(getActivity().getResources().getString(R.string.activity_add_new_question_maximum_value_hint));
solo.clickOnView(solo.getCurrentEditTexts().get(0));
solo.enterText(0, "34");
solo.clickOnView(solo.getCurrentEditTexts().get(0));
solo.enterText(1, "55");
// Click ok to close the dialog
solo.clickOnButton(getActivity().getResources().getString(R.string.OK));
// Click ok to get an ok message
solo.clickOnButton(getActivity().getResources().getString(R.string.OK));
//Wait for the ok toast message
boolean flagOKDatabase=solo.waitForText(getActivity().getResources().getString(R.string.database_success_storing_data),1,120);
assertEquals("Something wrong happened with the database", true, flagOKDatabase);
}
There is no downside to using robotium over the instrumentation framework native to android. That is because when using robotium you can still do everything you could of done without it but you also have access to lots of helpful functions already.
There are other android automation frameworks out there though that are definitely worth looking at. If you have any code in a web view these are especially helpful as this is where robotium really lets itself down.
https://github.com/calabash/calabash-android
https://github.com/calabash-driver/calabash-driver
http://testdroid.com/

Android Graphical UI Builders - Connect event

Maybe this question has been ask already, but could not find any answer for almost 2hours of internet search.
There is a graphical UI designer wich is coming along with the last android SDK.
Looks pretty cool and well done.
Nevertheless I * cannot find how to attach an event to the control through the graphical editor.
Of course I can add it manually into the xml, but in that case, what's the purpose of having such tool without that function ?
I mean all the other SDK I had in other languages always include that function.
I've also not been able to find doc about how to use this tool. Quite sad...
Thanks
If you want to add a click event handler, select the button (widget) in the GUI that you want to listen for, and look for the property onClick. Enter the name of the method you want to call when the user clicks on that widget, like.. onMyButtonClick
Then add the method to your Activity
public void onMyButtonClick(View v) {
// I heard the button click
}
The GUI builder is getting there, and is not yet as easy to use as the one in XCode, but it's not hard when you get used to it.

Categories

Resources