Correct way to check text of button
I need to check that English text on button is "Start this".
In string.xml
<string name="start">Start this</string>
So here Espresso test:
Approach#1
private val buttonStart = viewWithId(startButton)
#Test
fun buttonStartText() {
onView(withId(startButton))
.check(matches(withText(R.string.start)))
}
The test "buttonStartText" success pass. Nice.
Approach#2
Check hard code text.
#Test
fun buttonStartText() {
onView(withId(startButton))
.check(matches(withText("Start this")))
}
The test "buttonStartText" success pass. Nice.
The question is:
What approach is correct?
What I mean.
Suppose the developer made error and in string.xml change value of key "start" to "Start this 2222".
E.g.
<string name="start">Start this 2222</string>
As result the test in approach#1 is still pass. This is NOT CORRECT behavior.
But test in approach#2 is broken. This is a correct behavior.
As result I think test in approach#2 is correct way to check title/text on button.
Is I'm right?
P.S.
But if I has multi language app (e.g. 3 languages). In approach#1 nothing cha nge. But in in approach#2 I need to add two additional tests that check text in specific languages.
I think approach #1 is correct as you cover all the local's in that else in approach #2 you only cover english or specific language.
To be honest both approach are correct.
Because approach #1 checking if there is a button with text from R.strings..
but approach #2 checking if there is exactly that text.
So You are checking two different things. Depends what You want to test.
Related
In robolectric we can use annotations inside unit tests to test against specific display sizes/densities, like
#Config(qualifiers = "w2000dp")
My aim is that my test should only run when a specific condition is given (here: w2000dp).
I'm searching for a similar way to do this when testing with Espresso.
Currently I have two alternative (fall back) solution I could use to solve the issue, but maybe there is something more generic like in robolectric.
a) I can place a parameter inside ressource value folder (values-w1000dp, values-w2000dp).
I could read the value of that parameter (displayWidth) inside the test and decide whether to run the test or not.
values-w1000dp/strings.xml
<string name="displayWidth">1000dp</string>
values-w2000dp/strings.xml
<string name="displayWidth">2000dp</string>
b) I could read out the display size / density programmatically inside the test and decide, whether to run the test or not.
But both solutions feel wrong. Is there any other common way to solve this?
I really hope this question has not been answered everywhere else already but every search seems to focus on listeners and other uses of a button array but i want to also use it for formatting all buttons at the same time (activate, deactivate etc)
So here is what I have tried;
val buttons = arrayOf(btn1,btn2,btn3,btn4)
This will work, BUT will only change a single button
buttons[0].isEnabled=true; //
Then this is the bit that I am struggling with;
buttons[0..buttons.size].isEnabled=true;
The response is basically that it expects a single number and not a range.
I also tried;
buttons[].isEnabled=true;
The response is that it requires an index
I also tried
buttons.isEnabled=true;
This of course will not resolve properly
My key question really is can I apply formatting/state changes to all using an array or will I always have to do it for each button in turn?
I think it would be possible to create a loop but that isn't the route I wanted to follow here if there is an alternative
Don't think there is a way mate. You gotta loop and regardless of what syntatic sugar a language has in the end its still a for loop.
You could do:
buttons.forEach {
it.isEnabled = true
}
I have this simple Espresso interaction:
onView(atIndex(withId(R.id.editTextTextWidget), 0)).inRoot(authViewRootMatcher)
.check(matches(allOf(isDisplayed(), isEnabled())))
.perform(typeText("1"));
The check(matches(allOf(isDisplayed(), isEnabled()))) passes as expected, but the following perform(typeText("1")) does not. I cannot figure out why, for the life of me.
So, I can't believe I'm asking this, but how in the name of Android do I use Espresso to type text into my EditText whose ID is R.id.editTextTextWidget?
I fixed the problem by splitting the check(...) call and perform(...) call:
onView(atIndex(withId(R.id.editTextTextWidget), 0)).inRoot(authViewRootMatcher)
.check(matches(allOf(isDisplayed(), isEnabled())));
onView(atIndex(withId(R.id.editTextTextWidget), 0)).inRoot(authViewRootMatcher)
.perform(typeText("1"));
For some reason this works, and the original doesn't. #GooglePlz
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
My requirement is to put in a place name in a text field and show that in the map, so i used geocomplete js, which works well.
Now my user should be able to put in user defined places like 'my house', for that I need to remove the geocomplete on clicking a 'x' button on top of the map.
How can I implement this?
Thanks in advance
I wouldn't customize the package! When a new version comes out, you'll have to make the same changes you before.
Since you haven't provided any code, I can give you an idea of what I've done with JQuery validation method overrides.
You'll simply have to find the listener (something like $('#listenToThis').on('click', function(){ doThings(); }) in the geocomplete.js file, then override it in a file that is included after geocomplete.
If you're using bundles, just include your file after the geocomplete listener response is defined.
So, after you find those, you can do something similar to the following:
$.validator.methods.number = function (value, element) {
value = floatValue(value);
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
}
The function above allowed me to validate client-side numbers that were formatted as currency in JQuery ($).
This overrides the JQuery.validator.methods.number function (a cheating way to override the function without changing the package code), so if you can pinpoint the geocomplete.addSomethingToMap or geocomplete.reactToClick function, you should override it and it will essentially work that way.
Warning, though: you will need to reinclude the changes when you want to reenable the feature. You'll have to override, override, override again. This may not be the best way if they're going to be adding hundreds of different new locations on one screen, but for up to a small unit, such as a dozen, it should be a good solution.