Understanding results of Android Espresso instrumentation test - android

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.

Related

Android Espresso onView stuck infinitely

Firstly, I've turned off all the animation settings:
Window animation scale
Transition animation scale
Animator duration scale
Recently I need to add Espresso test cases for an old Android project(5 years+), and I don't have enough time to digging around all the custom views.
After reading many other posts here, I know that custom views might trigger this one, Espresso: AppNotIdleException
I did try to read the threads info as Espresso test error: AppNotIdleException and espresso onView inconsistent performance, but it seems that's not easy to fetch info from it(146 threads in my case):
I tried to search the android:repeatCount in the code base(via This SO answer), once again, not related.
There're several QAs related to this issue, but it seems they're not applying to my specific case.
The layout file that I want to click is simple:
<RelativeLayout
android:id="#+id/home_tab_discover"
style="#style/home_tab_item">
<SomeCustomImageView
android:id="#+id/home_tab_icon_discover"
...
The espresso case:
#Test
fun test_shelf_enter() {
Espresso.onView(ViewMatchers.withId(R.id.home_tab_icon_discover)).perform(ViewActions.click())
}
Then it launch the app, and I did see all the elements in the activity, but the Espresso waited infinitely.
It totally seems something related with your production code. I would say you should check the app itself. Maybe debugging what happens there? What's happening in the Activity you are testing?

Android Spinner Dismissed Right After Tapping in Espresso Tests

I have a suite of Espresso tests that run on Android. They generally run without issue. However, intermittently, they fail to validate date in a Spinner. By looking in to it I found out that somehow the Spinner is being dismissed as soon as it is tapped.
The code that is running is:
public static void selectFromComboBox(String prompt, String toSelect) {
onView(allOf(withId(R.id.combo_box_entry), hasSibling(withText(prompt)))).perform(click());
onData(Matchers.allOf(is(instanceOf(String.class)), is(toSelect))).perform(click());
}
When I run the exact same test with no changes, I sometimes get the error
android.support.test.espresso.PerformException: Error performing 'load adapter data' on view 'is assignable from class: class android.widget.AdapterView'.
I recorded the screen for both passes and fails and found that on the failures, the list for the Spinner is being dismissed almost as soon as it is opened which appears to be what is causing the problem.
Log output doesn't actually look any different between the pass scenarios and the fail scenarios. Has anybody seen this before or know of a work around or have any idea just what the heck is going on?
Not the prettiest solution but the way I worked around this was by adding a simple 500 ms wait after opening the menu

Testing two activities simultaneously with Espresso

I am writing an instrumentation test for an assignment for an Android course. I'd like to be able to check that, for example, a TextView is correctly positioned on the screen. To do this, I want to compare the position of the TextView to the position of a similar TextView in a reference implementation; something like this:
onView(withId(R.id.text_view))
.check(isBottomAlignedWith(onView(withId(R.id.text_view_solution))
The issue I'm running into is that R.id.text_view and R.id.text_view_solution exist in separate activities. I create two activity rules, one for the assignment solution and one for student submission:
#Rule
public ActivityTestRule<MainActivity> mSubmission = new ActivityTestRule<>(MainActivity.class);
#Rule
public ActivityTestRule<MainActivitySolution> mSolution = new ActivityTestRule<>(MainActivitySolution.class);
Previously, I've been simply getting the activities associated with the above rules and traversing the hierarchy myself to check, but it would be nice to be able to use convenience methods like isBottomAlignedWith. Additionally, I wanted to switch to Espresso as it's the only way I can find to test in landscape (something I'll also need to do).
However, for some reason, onView will only search the most recently created activity. So I can find views in MainActivitySolution but not in MainActivity. Is there a way do allow Espresso to search both activities for views?

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

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

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/

Categories

Resources