100
Testy UI w Espresso z farmą w tle

[Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Embed Size (px)

Citation preview

Page 1: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Testy UI w Espresso z farmą w tle

Page 2: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O nas

Mateusz Boś

[email protected]

matbos

Michał Górski

[email protected]

mgorski-zlatan

Page 3: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

Page 4: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

● PageObject

Page 5: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

● PageObject

● Intent Matching/Stubing

Page 6: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

● PageObject

● Intent Matching/Stubing

● Idling Resource

Page 7: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

● PageObject

● Intent Matching/Stubing

● Idling Resource

● Testy

Page 8: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

O czym będziemy mówili?

● Espresso○ Custom View Action

○ Custom View Matcher

● PageObject

● Intent Matching/Stubing

● Idling Resource

● Testy

● Uruchamianie testów w chmurze - AWS Device Farm i Firebase Test Lab

Page 9: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

Espresso.

Page 10: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

Espresso.onView()

Page 11: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView( )

Page 12: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button))

Page 13: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button))

ViewInteraction

ViewMatcher

Page 14: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button))

ViewInteraction

ViewMatcher

NoMatchingViewException - gdy nie ma widoku, który opisaliśmy

Page 15: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button))

ViewInteraction

ViewMatcher

NoMatchingViewException - gdy nie ma widoku, który opisaliśmy

AmbiguousViewMatcherException - gdy nasz opis pasuje do dwóch i więcej widoków

Page 16: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers

ViewInteraction loginButton = onView(withText("LOGGA IN"))

Page 17: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers

ViewInteraction passwordInput = onView(withHint("Lösenord"))

Page 18: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers

ViewInteraction loginButton = onView(withId(R.id.loginBtn))

Page 19: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Layout Inspector

Page 20: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers - złożone

allOf(Matcher<? super T>... matchers) - każdy z podanych

Page 21: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers - złożone

allOf(Matcher<? super T>... matchers) - każdy z podanych

allOf(

withText("Help me!"),

withHint("Attention"),

hasSibling(

withId(R.id.sos_button)

)

)

Page 22: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers - złożone

anyOf(Matcher<? super T>... matchers) - chociaż jeden z podanych

Page 23: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers - złożone

anyOf(Matcher<? super T>... matchers) - chociaż jeden z podanych

anyOf(

withText("Help me!"),

withHint("Attention"),

hasSibling(

withId(R.id.sos_button)

)

)

Page 24: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewMatchers

● isDisplayed

● isEnabled

● hasFocus

● isChecked

● withParent

● withChild

● hasIMEAction

● supportsInputMethod

● hasErrorText

● hasLinks

● isAssignableFrom

● isChecked

● isClickable

● ...

Page 25: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform( );

Page 26: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

Page 27: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)) .perform(click());

ViewInteraction

ViewActionViewMatcher

Page 28: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewActions

ViewInteraction loginButton = onView(withId(R.id.loginBtn))

loginButton.perform(click())

Page 29: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewActions

ViewInteraction passwordInput =

onView(withId(R.id.passwordTI))

passwordInput.perform(clearText())

passwordInput.perform(typeText(personalNumber),

closeSoftKeyboard())

Page 30: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewActions

● doubleClick

● longClick

● swipeLeft

● pressBack

● pressBackUnconditionally

● openLink

● pressKey

● pressMenuKey

● scrollTo

● swipeUp

● typeText

● ...

Page 31: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

onView(withId(R.id.msg))

Page 32: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

onView(withId(R.id.msg)).check( );

Page 33: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

onView(withId(R.id.msg)).check(matches( ));

Page 34: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

onView(withId(R.id.msg)).check(matches(isDisplayed()));

Page 35: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

onView(withId(R.id.button)).perform(click());

onView(withId(R.id.msg)).check(matches(isDisplayed()));

ViewAssertion

Page 36: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewAssertions

ViewInteraction toggle = onView(withId(R.id.toggle))

toggle.check(matches(isChecked()))

Page 37: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

ViewAssertions

● matches()

● doesNotExist()

● ViewMatchers

Page 38: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Espresso - Core API

ViewInteraction - Obiekt pośredni interakcji

ViewMatcher - pozwala znajdować widoki

ViewAction - pozwala wykonywać akcje na widokach

ViewAssertion - pozwala opisywać wymagania dla widoku

Cheat sheet - https://developer.android.com/training/testing/espresso/cheat-sheet.html

Page 39: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - scroll and click

Page 40: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - scroll and click

public static ViewAction scrollAndClick() {return new ViewAction() {

@Overridepublic String getDescription() {

return "Scroll and click";}

@Overridepublic Matcher<View> getConstraints() {

return ViewMatchers.isAssignableFrom(View.class);}

@Overridepublic void perform(UiController uiController, View view) {

scrollTo().perform(uiController, view);click().perform(uiController, view);

}

};}

Page 41: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - scroll and click

ViewInteraction button =

onView(withId(R.id.registerBtn))

button.perform(scrollAndClick())

Page 42: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - type text and close keyboard

Page 43: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - type text and close keyboard

public static ViewAction typeTextAndCloseKeyboard(String stringToBeTyped) {return new ViewAction() {

@Overridepublic void perform(UiController uiController, View view) {

scrollTo().perform(uiController, view);clearText().perform(uiController, view);if (stringToBeTyped.length() > 0) {

typeText(stringToBeTyped).perform(uiController, view);}closeSoftKeyboard().perform(uiController, view);

}…

};}

Page 44: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - type text and close keyboard

ViewInteraction passwordInput =

onView(withId(R.id.passwordTI))

passwordInput.perform(

typeTextAndCloseKeyboard())

Page 45: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - spinner

Page 46: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - spinner

void selectSpinnerItem(String text, Matcher<View> spinnerItemMatcher, ViewInteraction spinnerInteraction) {

spinnerInteraction.perform(scrollAndClick());try {

onData(is(text)).perform(click());} catch(Exception e) {

// if spinner was not opened properly we must retryselectSpinnerItem(text, spinnerItemMatcher, spinnerInteraction);

}

onView(spinnerItemMatcher)// if spinner was not checked properly we must retry.withFailureHandler((error, viewMatcher) -> selectSpinnerItem(...)).check(matches(withText(text)));

}

Page 47: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewActions - spinner

selectSpinnerItem(otherLoansCost,withIdInParentWithId(R.id.otherLoansCostLSp, android.R.id.text1), otherLoansCostLSpinnerInteraction);

Page 48: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - podstawy

BaseMatcher<T> - Klasa bazowa dla wszystkich matcherów

Page 49: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - podstawy

BaseMatcher<T> - Klasa bazowa dla wszystkich matcherów

TypeSafeMatcher<T> - null safe

Page 50: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - podstawy

BaseMatcher<T> - Klasa bazowa dla wszystkich matcherów

TypeSafeMatcher<T> - null safe

BoundedMatcher<T,S extends T> - Matcher(z Espresso) dla typu T, który pozwala nam zawęzić

obiekt do pewnego podtypu T np. BoundedMatcher<View, TextView>

Page 51: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - visibility

public static Matcher<View> isVisible() {

return new TypeSafeMatcher<View>() {

@Override

public boolean matchesSafely(final View view){

return

withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)

.matches(view);

}

};

}

Page 52: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - has background color

Matcher<View> hasBackgroundColor(@ColorRes final int colorId) {

return new TypeSafeMatcher<View>() {

@Override

public boolean matchesSafely(final View view) {

if(view instanceof CardView) {

return ((CardView)view).getCardBackgroundColor().getDefaultColor() ==

view.getContext().getResources().getColor(colorId);

}

return ((ColorDrawable) view.getBackground()).getColor() ==

view.getContext().getResources().getColor(colorId);

}

};

}

Page 53: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Custom ViewMatchers - ProgressBar ;)

public static Matcher<View> hasProgress(final int progress) {

return new BoundedMatcher<View, ProgressBar>(ProgressBar.class) {

@Override

public boolean matchesSafely(ProgressBar view) {

return view.getProgress() == progress;

}

};

}

Page 54: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

Jego zadaniem jest ukrycie niepotrzebnego skomplikowania dostępu do kontrolek, a uwypuklenie logiki

biznesowej

Page 55: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

Jego zadaniem jest ukrycie niepotrzebnego skomplikowania dostępu do kontrolek, a uwypuklenie logiki

biznesowej

Najczęściej reprezentuje tylko elementy ważne z punktu widzenia użytkownika(Nie musi wiernie

odwzorowywać całego ekranu)

Page 56: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

Jego zadaniem jest ukrycie niepotrzebnego skomplikowania dostępu do kontrolek, a uwypuklenie logiki

biznesowej

Najczęściej reprezentuje tylko elementy ważne z punktu widzenia użytkownika(Nie musi wiernie

odwzorowywać całego ekranu)

Powinien w odpowiedzi na metody zwracać albo typy proste albo inne Page Objecty

Page 57: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

Jego zadaniem jest ukrycie niepotrzebnego skomplikowania dostępu do kontrolek, a uwypuklenie logiki

biznesowej

Najczęściej reprezentuje tylko elementy ważne z punktu widzenia użytkownika(Nie musi wiernie

odwzorowywać całego ekranu)

Powinien w odpowiedzi na metody zwracać albo typy proste albo inne Page Objecty

Może ale nie musi zawierać asercje dot. danego ekranu (zdania są podzielone)

Page 58: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

Page 59: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

}

Page 60: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

onView(ViewMatchers.withId(R.id. newUsernameEditText))

.check(matches(isDisplayed()));

}

Page 61: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

onView(ViewMatchers.withId(R.id. newUsernameEditText))

.check(matches(isDisplayed()));

Espresso.closeSoftKeyboard();

}

Page 62: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

onView(ViewMatchers.withId(R.id. newUsernameEditText))

.check(matches(isDisplayed()));

Espresso.closeSoftKeyboard();

Espresso.pressBack();

}

Page 63: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

onView(ViewMatchers.withId(R.id. newUsernameEditText))

.check(matches(isDisplayed()));

Espresso.closeSoftKeyboard();

Espresso.pressBack();

onView(ViewMatchers.withId(R.id.usernameEditText))

.check(matches(isDisplayed()));

}

Page 64: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Object

@Testpublic void openRegisterScreen() {

onView(ViewMatchers.withId(R.id.registerButton))

.perform(click());

onView(ViewMatchers.withId(R.id. newUsernameEditText))

.check(matches(isDisplayed()));

Espresso.closeSoftKeyboard();

Espresso.pressBack();

onView(ViewMatchers.withId(R.id.usernameEditText))

.check(matches(isDisplayed()));

}

@Testpublic void openRegisterScreen() {

new LoginPageObject().openRegisterScreen().validate().goBack().validate();

}

Page 65: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

Page 66: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

public ItemListPageObject login(String username, String password){

usernameEditText.perform(typeText(username));

passwordEditText.perform(typeText(password));

loginButton.perform(click());

return new ItemListPageObject();

}

Page 67: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

public ItemListPageObject login(String username, String password) {

usernameEditText.perform(typeText(username));

passwordEditText.perform(typeText(password));

loginButton.perform(click());

return new ItemListPageObject();

}

public RegisterPageObject openRegisterScreen() {

registerButton.perform(click());

return new RegisterPageObject();

}

Page 68: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

public ItemListPageObject login(String username, String password) {

usernameEditText.perform(typeText(username));

passwordEditText.perform(typeText(password));

loginButton.perform(click());

return new ItemListPageObject();

}

public RegisterPageObject openRegisterScreen() {

registerButton.perform(click());

return new RegisterPageObject();

}

public LoginPageObject validate() {

usernameEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(withInputType(INPUT_TYPE)));

loginButton.check(matches(isDisplayed()));

registerButton.check(matches(isDisplayed()));

return this;

}

Page 69: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

public ItemListPageObject login(String username, String password) {

usernameEditText.perform(typeText(username));

passwordEditText.perform(typeText(password));

loginButton.perform(click());

return new ItemListPageObject();

}

public RegisterPageObject openRegisterScreen() {

registerButton.perform(click());

return new RegisterPageObject();

}

public LoginPageObject validate() {

usernameEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(withInputType(INPUT_TYPE)));

loginButton.check(matches(isDisplayed()));

registerButton.check(matches(isDisplayed()));

return this;

}

public LoginPageObject loginWithError(String username,

String password) {

login(username, password);

return this;

}

Page 70: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Page Objectpublic class LoginPageObject {

private final ViewInteraction usernameEditText;

private final ViewInteraction passwordEditText;

private final ViewInteraction loginButton;

private final ViewInteraction registerButton;

public LoginPageObject() {

usernameEditText = onView(withId(R.id.usernameEditText));

passwordEditText = onView(withId(R.id.passwordEditText));

loginButton = onView(withId(R.id.loginButton));

registerButton = onView(withId(R.id.registerButton));

}

public ItemListPageObject login(String username, String password) {

usernameEditText.perform(typeText(username));

passwordEditText.perform(typeText(password));

loginButton.perform(click());

return new ItemListPageObject();

}

public RegisterPageObject openRegisterScreen() {

registerButton.perform(click());

return new RegisterPageObject();

}

public LoginPageObject validate() {

usernameEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(isDisplayed()));

passwordEditText.check(matches(withInputType(INPUT_TYPE)));

loginButton.check(matches(isDisplayed()));

registerButton.check(matches(isDisplayed()));

return this;

}

public LoginPageObject loginWithError(String username, String password) {

login(username, password);

return this;

}

public LoginPageObject validateError() {

onView(withText(R.string.loginError)).check(matches(isDisplayed()));

return this;

}

}

Page 71: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Klasa testowa - JUnit 4

@Rule - dostarcza rozszerzalny mechanizm zmiany zachowania testów

ActivityTestRule - zapewnia “świeże” activity przed każdym testem

ServiceTestRule - zapewnia “świeży” serwis przed każdym testem

Page 72: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Klasa testowa - JUnit 4

@Rule - dostarcza rozszerzalny mechanizm zmiany zachowania testów

ActivityTestRule - zapewnia “świeże” activity przed każdym testem

ServiceTestRule - zapewnia “świeży” serwis przed każdym testem

@RunWith - pozwala ustawić runner dla klasy testowej

Page 73: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Klasa testowa - JUnit 4

@Rule - dostarcza rozszerzalny mechanizm zmiany zachowania testów

ActivityTestRule - zapewnia “świeże” activity przed każdym testem

ServiceTestRule - zapewnia “świeży” serwis przed każdym testem

@RunWith - pozwala ustawić runner dla klasy testowej

@Before - oznaczenie metody wywoływanej przed każdym testem

Page 74: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Klasa testowa - JUnit 4

@Rule - dostarcza rozszerzalny mechanizm zmiany zachowania testów

ActivityTestRule - zapewnia “świeże” activity przed każdym testem

ServiceTestRule - zapewnia “świeży” serwis przed każdym testem

@RunWith - pozwala ustawić runner dla klasy testowej

@Before - oznaczenie metody wywoływanej przed każdym testem

@After - oznaczenie metody wywoływanej po każdym teście

Page 75: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Klasa testowa - JUnit 4

@Rule - dostarcza rozszerzalny mechanizm zmiany zachowania testów

ActivityTestRule - zapewnia “świeże” activity przed każdym testem

ServiceTestRule - zapewnia “świeży” serwis przed każdym testem

@RunWith - pozwala ustawić runner dla klasy testowej

@Before - oznaczenie metody wywoływanej przed każdym testem

@After - oznaczenie metody wywoływanej po każdym teście

@Test - oznaczenie metody testowej

Page 76: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

@RunWith(AndroidJUnit4.class)public class IncreaseCreditLineLimitTests {

@Rule public ActivityTestRule<LauncherActivity> activityRule = new ActivityTestRule<>(LauncherActivity.class, false, false);

@Beforepublic void setUp() {

TestCredwayApp.reloadComponent();TestCredwayApp.getComponent().inject(this);Espresso.registerIdlingResources(idlingResource);

userSessionSimulator.cleanSession();}

@Afterpublic void tearDown() {

Espresso.unregisterIdlingResources(idlingResource);}

@Testpublic void increaseLimitSuccessWithRatingDialog() {

String currentCreditLineLimitText = "15 000 kr";String chosenCreditLineLimitText = "16 000 kr";…

}}

Page 77: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

Weryfikacja intentów(matching) - sprawdzenie czy dany Intent został wysłany

intended(allOf(

hasAction(equalTo(Intent.ACTION_VIEW)),

hasCategories(hasItem(equalTo(Intent.CATEGORY_BROWSABLE))),

hasData(hasHost(equalTo("www.google.com"))),

hasExtras(allOf(

hasEntry(equalTo("key1"), equalTo("value1")),

hasEntry(equalTo("key2"), equalTo("value2")))),

toPackage("com.android.browser")));

Page 78: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

Stubowanie intentów, pozwala zwrócić odpowiednio spreparowaną odpowiedź na

dopasowany intent.

Intent resultData = new Intent();

String phoneNumber = "123-345-6789";

resultData.putExtra("phone", phoneNumber);

ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultData);

intending(toPackage("com.android.contacts")).respondWith(result);

Page 79: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

@Rule public IntentsTestRule<MyPagesActivity> testRule = new IntentsTestRule<>(MyPagesActivity.class, true, false);

@Inject UserSessionSimulator userSessionSimulator;

@Beforepublic void setUp() {

TestCredwayApp.reloadComponent();TestCredwayApp.getComponent().inject(this);userSessionSimulator.simulateUserSession();

}

@Testpublic void shouldStartDialerIntent() {

testRule.launchActivity(null);mockDialerIntent();new MyPagesPageObject()

.openDrawer()

.openPhone();verifyCalledDialerIntentWithPhoneUri("tel:08363773");

}

Page 80: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

@Rule public IntentsTestRule<MyPagesActivity> testRule = new IntentsTestRule<>(MyPagesActivity.class, true, false);

@Inject UserSessionSimulator userSessionSimulator;

@Beforepublic void setUp() {

TestCredwayApp.reloadComponent();TestCredwayApp.getComponent().inject(this);userSessionSimulator.simulateUserSession();

}

@Testpublic void shouldStartDialerIntent() {

testRule.launchActivity(null);mockDialerIntent();new MyPagesPageObject()

.openDrawer()

.openPhone();verifyCalledDialerIntentWithPhoneUri("tel:08363773");

}

Page 81: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

@Rule public IntentsTestRule<MyPagesActivity> testRule = new IntentsTestRule<>(MyPagesActivity.class, true, false);

@Testpublic void shouldStartDialerIntent() {

testRule.launchActivity(null);

mockDialerIntent();

new MyPagesPageObject().openDrawer().openPhone();

verifyCalledDialerIntentWithPhoneUri("tel:08363773");

}

Page 82: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Intent

@Rule public IntentsTestRule<MyPagesActivity> testRule = new IntentsTestRule<>(MyPagesActivity.class, true, false);

@Testpublic void shouldStartDialerIntent() {

testRule.launchActivity(null);

intending(hasAction(Intent.ACTION_DIAL)).respondWith(new ActivityResult(Activity.RESULT_OK, null));

new MyPagesPageObject().openDrawer().openPhone();

intended(allOf(

hasAction(Intent.ACTION_DIAL), hasData("tel:08363773")

));

}

Page 83: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resource

Dostępne są cztery podstawowe klasy pozwalające na synchronizację Espresso z

naszym kodem:

● CountingIdlingResource

Page 84: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resource

Dostępne są cztery podstawowe klasy pozwalające na synchronizację Espresso z

naszym kodem:

● CountingIdlingResource

● UriIdlingResource

Page 85: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resource

Dostępne są cztery podstawowe klasy pozwalające na synchronizację Espresso z

naszym kodem:

● CountingIdlingResource

● UriIdlingResource

● IdlingThreadPoolExecutor

Page 86: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resource

Dostępne są cztery podstawowe klasy pozwalające na synchronizację Espresso z

naszym kodem:

● CountingIdlingResource

● UriIdlingResource

● IdlingThreadPoolExecutor

● IdlingScheduledThreadPoolExecutor

Page 87: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resources public final class RxEspressoTransformer {

private final Observable.Transformer transformer;

public RxEspressoTransformer(CountingIdlingResource idlingResource) {

transformer = new Observable.Transformer<Observable, Observable>() {

@Override

public Observable<Observable> call(Observable<Observable> observableObservable) {

return observableObservable

.doOnSubscribe(idlingResource::increment)

.doAfterTerminate(idlingResource::decrement);

}

};

}

public <T> Observable.Transformer<T, T> apply() {

return (Observable.Transformer<T, T>) transformer;

}

}

Page 88: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resources public final class RxEspressoTransformer {

private final Observable.Transformer transformer;

public RxEspressoTransformer(CountingIdlingResource idlingResource) {

transformer = new Observable.Transformer<Observable, Observable>() {

@Override

public Observable<Observable> call(Observable<Observable> observableObservable) {

return observableObservable

.doOnSubscribe(idlingResource::increment)

.doAfterTerminate(idlingResource::decrement);

}

};

}

public <T> Observable.Transformer<T, T> apply() {

return (Observable.Transformer<T, T>) transformer;

}

}

Page 89: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resources

observable

.compose(espressoTransformer.apply())

public final class RxEspressoTransformer {

private final Observable.Transformer transformer;

public RxEspressoTransformer(CountingIdlingResource idlingResource) {

transformer = new Observable.Transformer<Observable, Observable>() {

@Override

public Observable<Observable> call(Observable<Observable> observableObservable) {

return observableObservable

.doOnSubscribe(idlingResource::increment)

.doAfterTerminate(idlingResource::decrement);

}

};

}

public <T> Observable.Transformer<T, T> apply() {

return (Observable.Transformer<T, T>) transformer;

}

}

Page 90: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resources

observable

.compose(espressoTransformer.apply())

public final class RxEspressoTransformer {

private final Observable.Transformer transformer;

public RxEspressoTransformer(CountingIdlingResource idlingResource) {

transformer = new Observable.Transformer<Observable, Observable>() {

@Override

public Observable<Observable> call(Observable<Observable> observableObservable) {

return observableObservable

.doOnSubscribe(idlingResource::increment)

.doAfterTerminate(idlingResource::decrement);

}

};

}

public <T> Observable.Transformer<T, T> apply() {

return (Observable.Transformer<T, T>) transformer;

}

}

public class IncreaseCreditLineLimitTests {

@Inject CountingIdlingResource idlingResource;

@Before

public void setUp() {

Espresso.registerIdlingResources(idlingResource);

}

}

Page 91: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Idling Resources

observable

.compose(espressoTransformer.apply())

public final class RxEspressoTransformer {

private final Observable.Transformer transformer;

public RxEspressoTransformer(CountingIdlingResource idlingResource) {

transformer = new Observable.Transformer<Observable, Observable>() {

@Override

public Observable<Observable> call(Observable<Observable> observableObservable) {

return observableObservable

.doOnSubscribe(idlingResource::increment)

.doAfterTerminate(idlingResource::decrement);

}

};

}

public <T> Observable.Transformer<T, T> apply() {

return (Observable.Transformer<T, T>) transformer;

}

}

public class IncreaseCreditLineLimitTests {

@Inject CountingIdlingResource idlingResource;

@Before

public void setUp() {

Espresso.registerIdlingResources(idlingResource);

}

}

Page 92: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Test ekranu formularza

@Rule public ActivityTestRule<ApplyFormActivity> activityRule = new ActivityTestRule<>(ApplyFormActivity.class);

@Testpublic void validateShowErrorOnWrongEmail() {

new ApplyFormPageObject().typePersonalNumber("198911069998").typeMobileNumber("0786545678").typeEmail("test@email").typeClearingNumber("5555").typeAccountNumber("8911069998").selectIncome("1 000 kr+").selectHousing("Home").selectHousingCost("1 000 kr+").selectOtherLoansCost("1 000 kr+").selectEmploymentForm("Freelancer").selectChildrenCount("0+").clickOnApplyButtonWrongInput().validateWrongEmailError();

}

Page 93: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Test procesu rejestracji

@Rule public ActivityTestRule<LauncherActivity> activityRule = new ActivityTestRule<>(LauncherActivity.class);

@Testpublic void applyForCreditLineConfirmationStep() {

new WelcomePageObject().validate().clickOnRegistrationButton().validate("30 000 kr", 21, 21).clickOnApplyButton().validate("30 000 kr", "10 kr")

… (wypełnianie formularza tak samo jak w poprzednim teście) …

.clickOnPositiveButtonConfirmation()

.validate()

.onBackClick()

.validate();}

Page 94: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

AWS Device Farm && Firebase Test Lab

Page 95: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Firebase Test Lab

Page 96: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle
Page 97: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Pytania

Page 98: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Useful resources

Espresso:

https://developer.android.com/training/testing/espresso/index.html

Espresso-Intents:

https://developer.android.com/training/testing/espresso/intents.html

Espresso-Cheat Sheet

https://developer.android.com/training/testing/espresso/cheat-sheet.html

Page 99: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle

Dzięki za uwagę!

Page 100: [Quality Meetup] M. Górski, M. Boś - Testy UI w Espresso z farmą w tle