În acest post, veți afla despre cum să scrieți testele UI cu cadrul de testare Espresso și să vă automatizați fluxul de lucru pentru testare, în loc să utilizați procesul manuale dificil și cu predispoziție la erori.
Espresso este un cadru de testare pentru scrierea testelor UI în Android. Conform documentelor oficiale, puteți:
Utilizați Espresso pentru a scrie teste concise, frumoase și fiabile cu UI Android.
Una dintre problemele cu testarea manuală este că poate fi consumatoare de timp și dificil de efectuat. De exemplu, pentru a testa un ecran de conectare (manual) într-o aplicație Android, va trebui să faceți următoarele:
usernameEditText
și passwordEditText
sunt vizibile. În loc să petrecem tot timpul acest test manual, ar fi mai bine să petrecem mai mult timp scriind codul care face ca aplicația să iasă în evidență de restul! Și, chiar dacă testarea manuală este obositoare și destul de lentă, este încă predispusă la erori și este posibil să pierdeți unele cazuri de colț.
Unele dintre avantajele testării automate includ următoarele:
În acest tutorial, vom afla despre Espresso integrand-o într-un proiect Android Studio. Vom scrie teste UI pentru un ecran de conectare și o RecyclerView
, și vom afla despre testarea intențiilor.
Calitatea nu este un act, este un obicei. - Pablo Picasso
Pentru a putea urma acest tutorial, veți avea nevoie de:
Un exemplu de proiect (în Kotlin) pentru acest tutorial poate fi găsit pe replica noastră GitHub, pentru a putea urmări cu ușurință.
Activați Android Studio 3 și creați un nou proiect cu o activitate goală numită Activitate principala
. Asigurați-vă că ați verificat Includeți suportul Kotlin.
AndroidJUnitRunner
După ce ați creat un nou proiect, asigurați-vă că adăugați următoarele dependențe din Biblioteca de suport pentru testare Android din aplicația dvs. build.gradle (deși Android Studio le-a inclus deja pentru noi). În acest tutorial, folosim cea mai recentă versiune de bibliotecă Espresso 3.0.2 (din această scriere).
android // ... defaultConfig // ... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // ... dependences // ... androidTestImplementation 'com.android.support.test.espresso: espresso-core: 3.0. 2 'șiroidTestImplementation' com.android.support.test: runner: 1.0.2 'androidTestImplementation' com.android.support.test: rules: 1.0.2 '
Am inclus și alergătorul de instrumente AndroidJUnitRunner
:
Un Instrumentaţie
care execută testele JUnit3 și JUnit4 împotriva unui pachet Android (aplicație).
Rețineți că Instrumentaţie
este pur și simplu o clasă de bază pentru implementarea codului de instrumentație aplicație.
Sincronizarea aplicației Espresso, care nu știe să aștepte terminarea unei animații, poate duce la eșecul unor teste - dacă permiteți animația pe dispozitivul de testare. Pentru a dezactiva animația pe dispozitivul dvs. de testare, accesați Setări > Opțiuni pentru dezvoltatori și dezactivați toate opțiunile de mai jos în secțiunea "Desenare":
În primul rând, începem să testați un ecran de conectare. Iată cum pornește fluxul de conectare: utilizatorul lansează aplicația, iar primul ecran afișat conține un singur Logare buton. Cand asta Logare butonul este apăsat, se deschide LoginActivity
ecran. Acest ecran conține doar două Editează textul
s (câmpurile de nume de utilizator și parola) și a A depune buton.
Iată ce e al nostru Activitate principala
aspectul arata ca:
Iată ce e al nostru LoginActivity
aspectul arata ca:
Să scriem acum un test pentru noi Activitate principala
clasă. Du-te la tine Activitate principala
clasa, mutați cursorul pe Activitate principala
numele și apăsați Shift-Control-T. Selectați Creați un test nou ... în meniul pop-up.
apasă pe O.K butonul și se va afișa un alt dialog. Alege androidTest și faceți clic pe O.K butonul încă o dată. Rețineți că, pentru că scriem un test de instrumentație (teste specifice SDK pentru Android), cazurile de testare se află în androidTest / java pliant.
Acum, Android Studio a creat cu succes o clasă de testare pentru noi. Deasupra numelui clasei, includeți această adnotare: @RunWith (AndroidJUnit4 :: clasă)
.
importați și clasa MainActivityTest clasa de import android.support.test.runner.AndroidJUnit4 org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class)
Această adnotare înseamnă că toate testele din această clasă sunt teste specifice pentru Android.
Pentru că vrem să testăm o Activitate, trebuie să facem o mică pregătire. Trebuie să informăm Espresso care Activitate de deschidere sau de lansare înainte de executarea și distrugerea după executarea oricărei metode de testare.
importand android.support.test.rule.ActivityTestRule import șiroid.support.test.run.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) clasă MainActivityTest @Rule @JvmField var activityRule = ActivityTestRule(MainActivity :: class.java)
Rețineți că @Regulă
adnotarea înseamnă că aceasta este o regulă de test JUnit4. Regulile de testare JUnit4 sunt executate înainte și după fiecare metodă de testare (adnotată cu @Test
). În scenariul nostru, dorim să lansăm Activitate principala
înainte de fiecare metodă de testare și distrugeți-o după.
Am inclus și @JvmField
Adnotarea Kotlin. Aceasta instruiește pur și simplu compilatorul să nu genereze getters și setteri pentru proprietate și în loc să-l expună ca un simplu câmp Java.
Iată trei etape majore în scrierea unui test Espresso:
TextView
sau Buton
) pe care doriți să le testați.Următoarele tipuri de adnotări pot fi aplicate metodelor utilizate în cadrul clasei de test.
@Înainte de curs
: aceasta indică faptul că metoda statică la care se aplică această adnotare trebuie să fie executată o dată și înainte de toate testele din clasă. Aceasta ar putea fi folosită, de exemplu, pentru a configura o conexiune la o bază de date. @Inainte de
: indică faptul că metoda la care se atașează această adnotare trebuie să fie executată înainte de fiecare metodă de testare din clasă.@Test
: indică faptul că metoda la care se atașează această adnotare ar trebui să ruleze ca un caz de test.@După
: indică faptul că metoda la care se atașează această adnotare ar trebui să ruleze după fiecare metodă de testare. @După clasa
: indică faptul că metoda la care se atașează această adnotare ar trebui să ruleze după ce au fost executate toate metodele de testare din clasă. Aici, de obicei, închidem resursele care au fost deschise @Înainte de curs
. Vedere
Utilizarea onView ()
În a noastră Activitate principala
layout file, avem doar un widget - Logare
buton. Să încercăm un scenariu în care un utilizator să găsească butonul și să facă clic pe el.
importand android.support.test.espresso.Espresso.onView importand clasa MainActivityTest si /roid.support.test.espresso.matcher.ViewMatchers.withId // ... @RunWith (AndroidJUnit4 :: class) // // ... @Test @Throws (Exceptie: : class) distracție clickLoginButton_opensLoginUi () onView (cuId (R.id.btn_login))
Pentru a găsi widget-uri în Espresso, facem uz de onView ()
metoda statică (în loc de findViewById ()
). Tipul de parametru pe care îl furnizăm onView ()
este a Matcher
. Rețineți că Matcher
API nu vine din SDK Android, ci din Proiectul Hamcrest. Biblioteca lui Hamcrest este cuprinsă în biblioteca Espresso pe care am tras-o prin Gradle.
onView (withId (R.id.btn_login))
va returna a ViewInteraction
că este pentru un Vedere
a cărui identitate este R.id.btn_login
. În exemplul de mai sus, am folosit withId ()
să căutați un widget cu un id dat. Alte vizualizări pe care le putem folosi sunt:
withText ()
: returnează o potrivire potrivită TextView
pe baza valorii sale de proprietate text.withHint ()
: returnează o potrivire potrivită TextView
bazat pe valoarea proprietății indiciu.withTagKey ()
: returnează o potrivire potrivită Vedere
pe baza cheilor de etichetare.withTagValue ()
: returnează o potrivire potrivită Vedere
s pe baza valorilor proprietății tag-urilor.Mai întâi, hai să verificăm dacă butonul este afișat de fapt pe ecran.
onView (withId (R.id.btn_login)). verifică (meciuri (isDisplayed ()))
Aici, confirmăm doar dacă butonul cu ID-ul dat (R.id.btn_login
) este vizibil pentru utilizator, deci folosim Verifica()
pentru a confirma dacă subiacentă Vedere
are o anumită stare - în cazul nostru, dacă este vizibilă.
chibrituri()
metoda statică returnează o generică ViewAssertion
care afirmă că există o viziune în ierarhia vizuală și că este potrivită de matricea vizuală dată. Acea vizualizare a matcherului este returnată prin apelare este afisat()
. După cum este sugerat de numele metodei, este afisat()
este o potrivire potrivită Vedere
s care sunt afișate în prezent pe ecran către utilizator. De exemplu, dacă vrem să verificăm dacă este activat un buton, pur și simplu treceți este activat()
la chibrituri()
.
Alte materiale de vizualizare populare pe care le putem trece în chibrituri()
sunt:
hasFocus ()
: returnează o potrivire potrivită Vedere
care se concentrează în prezent.este bifat()
: returnează o matrice care acceptă dacă și numai dacă vizualizarea este a CompoundButton
(sau subtipul) și este în stare verificată. Opusul acestei metode este isNotChecked ()
. este selectat()
: returnează o potrivire potrivită Vedere
s care sunt selectate.Pentru a executa testul, puteți face clic pe triunghiul verde de lângă metoda sau numele clasei. Dacă faceți clic pe triunghiul verde de lângă numele clasei, veți rula toate metodele de testare din clasa respectivă, în timp ce cel de lângă o metodă va executa testul numai pentru acea metodă.
Ura! Testul nostru a trecut!
Pe o ViewInteraction
obiect care este returnat prin apelare onView ()
, putem simula acțiunile pe care un utilizator le poate efectua pe un widget. De exemplu, putem simula o acțiune de clic prin simpla apelare a acesteia clic()
metoda statică în interiorul ViewActions
clasă. Aceasta va reveni a ViewAction
obiect pentru noi.
Documentația spune asta ViewAction
este:
Responsabil pentru realizarea unei interacțiuni pe elementul Vizualizare dat.
@Test distractiv clickLoginButton_opensLoginUi () // ... onView (cuId (R.id.btn_login)) efectuați (faceți clic pe ())
Realizăm un eveniment de clic efectuând o primă apelare a executa()
. Această metodă efectuează acțiunea (acțiunile) dată în vizualizarea selectată de vizualizatorul curent. Rețineți că putem transmite o singură acțiune sau o listă de acțiuni (executate în ordine). Aici am dat-o clic()
. Alte acțiuni posibile sunt:
TYPETEXT ()
pentru a imita textul într-un text Editează textul
.text clar()
pentru a simula textul de compensare într - un Editează textul
.dublu click()
pentru a simula dublu-clic pe Vedere
.longClick ()
pentru a imita cu un clic lung Vedere
.scrollTo ()
pentru a simula defilarea a ScrollView
la un anumit Vedere
care este vizibil. swipeLeft ()
pentru a simula rotire spre dreapta spre stânga peste centrul vertical al unui Vedere
.Mai multe simulări pot fi găsite în interiorul ViewActions
clasă.
Să terminăm testul, pentru a valida acest lucru LoginActivity
ecranul este afișat ori de câte ori Logare butonul este apăsat. Deși am văzut deja cum să folosim Verifica()
pe o ViewInteraction
, să-l folosim din nou, să-l trecem pe altul ViewAssertion
.
@Test distractiv clickLoginButton_opensLoginUi () // ... onView (cuId (R.id.tv_login)) check (se potrivește (isDisplayed ()))
În interiorul LoginActivity
layout file, în afară de Editează textul
s și a Buton
, avem de asemenea un TextView
cu ID R.id.tv_login
. Deci, pur și simplu facem un cec pentru a confirma că TextView
este vizibil pentru utilizator.
Acum puteți trece din nou la test!
Testele dvs. ar trebui să treacă cu succes dacă ați urmat corect toate etapele.
Iată ce sa întâmplat în timpul procesului de efectuare a testelor noastre:
Activitate principala
folosind activityRule
camp.R.id.btn_login
) a fost vizibilă (este afisat()
) către utilizator.clic()
) pe acel buton.LoginActivity
a fost arătată utilizatorului prin verificarea dacă a TextView
cu id R.id.tv_login
în LoginActivity
este vizibil.Puteți să consultați întotdeauna foaia de înșelătorie Espresso pentru a vedea diferitele materiale de vizualizare, acțiunile de vizualizare și afirmațiile de afirmații disponibile.
LoginActivity
EcranIată-ne pe noi LoginActivity.kt:
import android.os.Bundle import șiroid.support.v7.app.AppCompatActivity import android.widget.Button de import android.widget.EditText de import android.widget.TextView clasa LoginActivity: AppCompatActivity () privat lateinit var usernameEditText: EditText privat lateinit var loginTitleTextView: TextView privat lateinit var parolaEditText: EditText privat lateinit var submitButton: buton override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_login) usernameEditText = findViewById (R.id.et_username) passwordEditText = findViewById (R.id.et_password) submitButton = findViewById (R.id.btn_submit) loginTitleTextView = findViewById (R.id.tv_login) submitButton.setOnClickListener dacă (usernameEditText.text.toString () == "chike" && passwordEditText. text.toString () == "parola") loginTitleTextView.text = "Succes" altceva loginTitleTextView.text = "Failure"
În codul de mai sus, dacă numele de utilizator introdus este "chike" și parola este "parola", atunci login-ul are succes. Pentru orice altă intrare, e un eșec. Să scriem acum un test Espresso pentru acest lucru!
Mergi la LoginActivity.kt, mutați cursorul pe LoginActivity
numele și apăsați Shift-Control-T. Selectați Creați un test nou ... în meniul pop-up. Urmați același proces ca și pentru noi MainActivity.kt, și faceți clic pe O.K buton.
importați android.support.test.espresso.Espresso importați android.support.test.espresso.Espresso.onVizualizați importul android.support.test.espresso.action.ViewActions importați android.support.test.espresso.assertion.ViewAssertions.matches importați android .support.test.espresso.matcher.ViewMatchers.withId import android.support.test.espresso.matcher.ViewMatchers.withText import android.support.test.rule.ActivityTestRule import android.support.test.run.runner.AndroidJUnit4 import org.junit .Rule de import org.junit.Test import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) clasa LoginActivityTest @Rule @JvmField var activityRule = ActivityTestRule(LoginActivity :: class.java) private val username = "chike" private val password = "password" @Test distractiv clickLoginButton_opensLoginUi () onView (cuId (R.id.et_username)) perform (ViewActions.typeText (username)) onView (cu Id (R.id.et_password)) executa (ViewActions.typeText (parola)) onView (withId (R.id.btn_submit)) executa (ViewActions.scrollTo (), ViewActions.click ()) Espresso.onView (cu Id (R.id.tv_login)) .check (se potrivește (cu Text ("Succes")))
Această clasă de testare este foarte asemănătoare cu prima noastră. Dacă conducem testul, a noastră LoginActivity
ecranul este deschis. Numele de utilizator și parola sunt introduse în R.id.et_username
și R.id.et_password
câmpuri respective. Apoi, Espresso va face clic pe A depune butonul (R.id.btn_submit
). Va astepta pana la Vedere
cu id R.id.tv_login
pot fi găsite cu citirea textului Succes.
RecyclerView
RecyclerViewActions
este clasa care expune un set de API-uri care să opereze pe RecyclerView
. RecyclerViewActions
face parte dintr-un artefact separat din interiorul espresso-contrib
artefact, care ar trebui, de asemenea, să fie adăugat la build.gradle:
androidTestImplementation 'com.android.support.test.espresso: espresso-contrib: 3.0.2'
Rețineți că acest artefact conține, de asemenea, API-ul pentru UI care testează sertarul de navigație DrawerActions
și DrawerMatchers
.
@RunWith (AndroidJUnit4 :: clasa) clasa MyListActivityTest // ... @Test fun clickItem () onView (cuId (R.id.rv)) .perform (RecyclerViewActions.actionOnItemAtPosition(0, ViewActions.click ())))
Pentru a face clic pe un element în orice poziție din a RecyclerView
, noi invocăm actionOnItemAtPosition ()
. Trebuie să-i dăm un tip de element. În cazul nostru, elementul este ViewHolder
clasă în interiorul nostru RandomAdapter
. Această metodă include și doi parametri; prima este poziția, iar a doua este acțiunea (ViewActions.click ()
).
Alte RecyclerViewActions
care pot fi efectuate sunt:
actionOnHolderItem ()
: efectuează a ViewAction
pe o vizualizare potrivită de viewHolderMatcher
. Acest lucru ne permite să ne potrivim cu ceea ce este conținut în interiorul ViewHolder
mai degrabă decât poziția. scrollToPosition ()
: returnează a ViewAction
care derulează RecyclerView
într-o poziție.Înainte (odată ce ecranul "adăugați nota" este deschis), vom introduce textul notei noastre și vom salva nota. Nu trebuie să așteptăm deschiderea noului ecran - Espresso va face acest lucru automat pentru noi. Se așteaptă până la o vizualizare cu id-ul R.id.add_note_title
poate fi găsit.
Espresso folosește un alt artifact numit espresso-intențiile
pentru testarea intențiilor. Acest artefact este doar o altă extensie la Espresso care se axează pe validarea și batjocurarea intențiilor. Să ne uităm la un exemplu.
În primul rând, trebuie să tragem espresso-intențiile
biblioteca în proiectul nostru.
androidTestImplementation 'com.android.support.test.espresso: espresso-intentions: 3.0.2'
importați android.support.test.espresso.intent.rule.IntentsTestRule importați șiroid.support.test.run.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) clasa PickContactActivityTest @ Regula @JvmField var intentRule = IntentsTestRule(PickContactActivity :: class.java)
IntentsTestRule
extinde ActivityTestRule
, astfel încât ambii au comportamente similare. Iată ce spune docul:
Această clasă este o extensie aActivityTestRule
, care inițializează intențiile espresso înainte de fiecare încercare adnotată cuTest
și eliberează intențiile Espresso după fiecare test. Activitatea va fi terminată după fiecare test și această regulă poate fi utilizată în același mod ca șiActivityTestRule
.
Principala caracteristică de diferențiere este că dispune de funcționalități suplimentare pentru testare startActivity ()
și startActivityForResult ()
cu batjocuri și bătăi de cap.
Vom încerca acum un scenariu în care un utilizator va face clic pe un buton (R.id.btn_select_contact
) de pe ecran pentru a alege un contact din lista de contacte a telefonului.
// ... @ Test de distracție stubPick () var rezultat = Instrumentation.ActivityResult (Activity.RESULT_OK, Intent (null, ContactsContract.Contacts.CONTENT_URI)) intenționează (hasAction (Intent.ACTION_PICK) R.id.btn_select_contact)) efectuați (clic ()) intenționat (allOf (toPackage ("com.google.android.contacts"), hasAction (Intent.ACTION_PICK), hasData (ContactsContract.Contacts.CONTENT_URI) ...
Aici folosim intenționând ()
de la espresso-intențiile
biblioteca pentru a crea un stub cu un raspuns fals pentru noi ACTION_PICK
cerere. Iată ce se întâmplă PickContactActivity.kt când utilizatorul face clic pe butonul cu id R.id.btn_select_contact
pentru a alege un contact.
Distracție pickContact (v: Vizualizare) val i = Intent (Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI) startActivityForResult (i, PICK_REQUEST)
intenționând ()
ia un a Matcher
care se potrivește cu intențiile pentru care ar trebui furnizat un răspuns stubat. Cu alte cuvinte, Matcher
identifică ce solicitare doriți să faceți. În cazul nostru, ne folosim hasAction ()
(metoda de ajutor în IntentMatchers
) pentru a ne găsi ACTION_PICK
cerere. Apoi, invocăm Raspunsulcu ()
, care stabilește rezultatul pentru onActivityResult ()
. În cazul nostru, rezultatul a fost Activity.RESULT_OK
, simulând utilizatorul selectând un contact din listă.
Apoi, simulam click pe butonul de selectare a contactelor, care sună startActivityForResult ()
. Rețineți că grupul nostru a trimis răspunsul fals onActivityResult ()
.
În cele din urmă, folosim destinate ()
metoda de ajutor pentru a valida pur și simplu că apelurile către startActivity ()
și startActivityForResult ()
au fost făcute cu informațiile corecte.
În acest tutorial, ați învățat cum să utilizați cu ușurință cadrul de testare Espresso în proiectul dvs. Android Studio pentru a vă automatiza fluxul de lucru pentru testare.
Vă recomandăm să verificați documentația oficială pentru a afla mai multe despre scrierea testelor UI cu Espresso.