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.
Related
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
I am working on my second Android Application, first being, hello world. The application code is quite crazy looking because I love to test new libraries and ideas in it. I have been working on this application for well over 3 months and one of my activities is getting way to large and difficult to work with. I find myself getting lost in the code and it is taking longer to do simple things. There might be simple solutions to solving this issue. I really want to split my activity into two and reference each other if possible. Is there are any suggestions to simplifying and organizing code that would be greatly helpful. Even example will help me very much.
Part of my activity is adding a ton of data into a database and the other part is a long equation with multiple values. Another part is implementing the HoloGraphLibrary (Which I love). It is also implementing a listView with custom adapter. It also has a custom dialog............ I can go on and on. I hope you get my point.
EDIT
Going to work with this.
HoloGraphHelper holoGraph = new HoloGraphHelper();
holoGraph.initialize();
Try creating classes for each responsibility.
A Database Helper that has functions to insert data too:
DatabaseHelper database = new DatabaseHelper();
database .insertData(whatever);
A HoloGraphHelper that initializes the HoloGraph
HoloGraphHelper holoGraph = new HoloGraphHelper();
holoGraph.initialize();
And so on.
Break into multiple files. First classes defined in the Activity like the adapter. Change anonymous classes to classes defined in their own file. Look for ways to break out other related code into a class.
Right click on src folder of your Project and select new - class to create a new class. You can use a class for storing methods but you won't be able to display anything on screen.
To display contents to user, you can create a new Activity bu pressing Ctrl + N and selecting Android - Android Activity.
The best way is modularise your code.
I.e split your code into various related modules, for example a separate class for each part that your testing. So you could have a database entry class, a class for Gui testing, i.e. for your custom dialog. That class does all the work for that test, into various functions, I always try to keep functions as small as possible as they are easy to read.
As an example for your database entry, you could have a function which checks the database if the record already exists and then insert it. But a better way would be your insert function only performs the insert code and instead within this function it calls CheckIfDatAlreadyExists function which can return a bool so you know whether you should go ahead and insert the record. This would keep the code tidy and clean to manage.
Then from your main activity all would need to do is instantiate the relative class and call the relevant method.
I've been working on android for few months, during my spare time, so probably i'm going to ask stupid questions.
In a class, I create an object, for example a button, and I'd like to 'reach' it from others classes.
Of course i could do it this way:
public class CreateButton{
public void createButton(){
Button myButton = new Button(context);
//I can "pass" my object to another class this way
ManageButton manageButton = new ManageButton(myButton);
// or this way
manageButton.writeButtonTextMethod(myButton);
}
}
or this way
public class CreateButton{
public Button createButton(){
Button myButton = new Button(context);
return myButton;
}
}
public class ManageButton{
public void writeButtonTextMethod(){
CreateButton createButton = new CreateButton()
Button myButton = createButton.createButton();
myButton.setText("W");
}
}
I'm wondering if there's a way to 'reach' myButton, created in createButton.class directly from ManageButton.class (and other classes).
With the code above, I have to 'call' createButton.class from ManageButton.class or vice versa to be allowed to manage myButton.
I can do it making myButton static, but it's not correct make view static.
For example, i can easily reach variables created in a class that extends Application. Is there something similar for views?
Much people/guides/articles claim is possible and correct sharing objects (and fields) between activities by using Application, this way:
Such people are idiots, as myButton will introduce a memory leak unless this is done very carefully.
So, I'm still not sure how i should share objects (TextView, Button, ImgaeView etc.) among different activities.
You don't share widgets between activities. At most, you share model data between activities (or, better yet, pointers to centrally-stored model data).
In a Web app, you do not share DOM nodes between Web pages, as that is not possible. At most, you share data passed as GET parameters and the like between Web pages, or you store data in local central storage (cookies, local storage, IndexedDB, etc.). Android activities are like Web pages -- they are loosely coupled.
If you have pieces of your UI that are so tightly coupled that they absolutely need to share widgets, they should not be separate activities.
Views are not meant to be shared across Activities. Even if you do disregard this and share them, you're more than likely to run into a WindowManager$BadTokenException, which essentially means you can't alter Views outside of its parent Activity.
However, to answer your question in a more general way about sharing non-view objects, the Application class is the right way to go. However, making the objects static is not the right way to do. Instead, make them instance variables, and access them in in your various activities by using:
Application application:
//Below line is in onCreate()
application = this.getApplication();
After that, simple access it using
application.myObject; //Or you could use getters and setters. Your choice.
Not quite sure why would u like to share one button between two activities?!
It is either button in ActivityA or ActivityB? No need, or use to do it..You shouldn't do it.
EDIT: This is more Java Basics topic then Android.. And from what I understood, u call "class" something that IMHO is method (because u can't CALL the class and because what did you described).
Best advices I can give u here:
Study Java basics (methods, classes, objects, constructors, inheritance..),
Study Android basics (simple examples with 1 activity til u understand it good),
Make MyButton Class which will extend standard button,
Create button(s) and change their properties
This few tasks will make u busy for some time. Take it easy because it will be easier later with complicated apps. Hope I helped you. Cheers
EDIT2: In last example u wrote (in comments to this answer), nothing make sense because as we wrote u before u don't share button between two activities. Pause! Read it again: You should't share button between two Activies. Questions u asked are not clear because for A and Z u can't say just class and "share" button between them. Which class? no line of code written so it is really difficult to figure out what u want.. :s
LAST LAST EDIT: Man.. Please understand that u r missing basics of Java. No bad intentions here, just try to guide u and tell u exactly what u should study. Why? Because it doesn't surprise me that you are confused to explain what u mean when classes are with one method named exactly the same.. They are just objects "doing something", u want to "manage" them by adding variable in constructor which doesn't exist etc.. Learn, next time write immediately code to save time for all of us. Cheers and enjoy learning
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.
OOTB, Robolectric does not support Locales that well. Therefore, if your app is dependent on locales (which a lot of apps are if they are i18n'nd properly) this can be a royal pain. Long story short, I created my own ShadowFooGeocoder and ShadowFooAddress that allow me to simulate the locale I want. They're basically re-implementations of the existing shadows.
However, when I bind my class as such: bindShadowClass(ShadowFooGeocoder.class), this works great. At runtime, the correct shadow is returned. The problem is that I want to set up the simulations on this object and I'm not sure how. shadowOf(instance) where instance is an injected GeoCoder returns ShadowGeoCoder. I've tried working directly with the ShadowWrangler, but that also returns a ShadowGeocoder.
How can I get at my shadowed class that I've bound through the bindShadowClass(...) call so I can set my expectations (simulations)?
Note: This is a repost of the same question on the Robolectric group here. I posted here because my success rate of getting anyone to answer questions on the group is fairly low. I'm hoping for a better result here.
What I've basically done here is extend ShadowGeocoder like this:
#SuppressWarnings({"UnusedDeclaration"})
#Implements(Geocoder.class)
public class ShadowFooBarGeocoder extends ShadowGeocoder {
// implementation stuff
}
Then I would bind it using the bindShadowClasss(...) and when I retreive the shadow via the static shadowOf(...) call I get back a "ShadowGeocoder" which is an instance of ShadowFooBarGeocoder. I then cast it to that type and perform whatever work I need to.