Android Espresso
在本教程中,我们将讨论用于测试Android应用程序UI的Espresso测试框架。
我们将创建一个基本的登录应用程序,并进行一些Espresso测试。
Android Espresso
之前我们已经讨论过Android单元测试。
测试是制造任何产品的重要领域。
它可以帮助我们:
检测我们的代码未涵盖的错误/案例。
尽量减少手动用户测试。
无需一次又一次地测试较早的功能。构建涵盖最终案例的强大应用程序。
Android中的测试主要涉及两种类型:
- 单元测试
- 仪器测试
Espresso测试属于第二种类型。
它通过编写简短的Android UI测试来进行自动化的UI测试。
让我们看一下Espresso工具测试框架的组成部分。
ViewMatchers,ViewActions,ViewAssertions
ViewMatchers –允许我们在视图层次结构中查找视图。
ViewActions –允许我们对视图执行自动操作。
例如点击次数等ViewAssertions –允许我们声明视图的状态。
浓咖啡测试的基本框架代码是:
onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion)
在onView内部,我们寻找View的ID。
可以使用以下方法来完成:
withId()
–传递唯一的ID- withText()-传递视图的文本。
它搜索具有指定文本的视图。
在" perform"内部,我们传递要在View上完成的操作。
例:
click()–单击在onView中传递的视图。
typeText()–传递要在View中输入的字符串。
特别是在EditText中使用。replaceText()
–用传递的字符串替换当前文本。closeSoftKeyboard()
–关闭键盘。
在check
内部,我们主要使用以下方法来声明状态:
matches
doesNotExist
我们也可以使用Espresso测试来测试视图层次。
例如,我们可以通过以下方式定位视图断言:
onView(withId(R.id.textView)).check(isRightOf(withText("Hello World"))); onView(withId(R.id.textView)).check(isBelow(withText("Hello World")));
位置声明的其他方法有:
isLeftAlignedWith isAbove isTopAlignedWith
Hamcrest Matchers是非常强大的Matchers。
以下示例显示:
onView(withText(startsWith("Hello"))).perform(click()); onView(allOf(withId(R.id.textView),isDisplayed())); onView(allOf(withId(R.id.textView),hasLinks()));
最后一个检查整个TextView是否为链接。
在以下部分中,我们将创建一个android应用程序并编写我们的第一个意式浓缩咖啡测试。
我们的Android应用程序将包含一个基本的登录表单,其中包含用户名,电话号码和确认号码字段。
按下登录按钮后,如果电话号码字段不匹配,我们将显示一条Toast消息。
如果一切正常,我们将通过Hello向用户推荐。
Espresso测试入门
在build.gradle文件中添加以下依赖项:
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
在defaultConfig块中添加以下内容:
android{ ... defaultConfig{ ... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } ... }
AndroidJUnitRunner是工具运行器。
这是运行Android测试的切入点。
以下是我们应用的" build.gradle"文件的外观:
确保在运行Espresso测试之前禁用Android设备中的"动画"。
否则,默认的屏幕动画可能会干扰测试。
您可以从"开发人员选项"中禁用它们
代码
下面给出了" activity_main.xml"布局的代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:layout_margin="8dp" android:orientation="vertical" tools:context=".MainActivity"> <EditText android:id="@+id/inUsername" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Username" <EditText android:id="@+id/inNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Number" android:inputType="number" <EditText android:id="@+id/inConfirmNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Confirm Number" android:inputType="number" <Button android:id="@+id/btnLogin" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="LOGIN" <TextView android:id="@+id/txtLoginResult" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:gravity="center" </LinearLayout>
MainActivity.java类的代码如下:
package com.theitroad.androidexpressobasics; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { EditText inUsername, inNumber, inConfirmNumber; Button btnLogin; TextView txtLoginResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); inUsername = findViewById(R.id.inUsername); inNumber = findViewById(R.id.inNumber); inConfirmNumber = findViewById(R.id.inConfirmNumber); btnLogin = findViewById(R.id.btnLogin); txtLoginResult = findViewById(R.id.txtLoginResult); btnLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (TextUtils.isEmpty(inNumber.getText())) Toast.makeText(getApplicationContext(), R.string.number_empty, Toast.LENGTH_SHORT).show(); else if (!(inNumber.getText().toString().equals(inConfirmNumber.getText().toString()))) Toast.makeText(getApplicationContext(), R.string.toast_error, Toast.LENGTH_SHORT).show(); else if (inUsername.getText().toString().trim().length() == 0) Toast.makeText(getApplicationContext(), R.string.username_empty, Toast.LENGTH_SHORT).show(); else txtLoginResult.setText("Hello " + inUsername.getText().toString().trim()); } }); } }
下面给出了strings.xml文件的代码:
编写Espresso测试
Espresso测试写在src中| androidTest文件夹文件。
默认情况下,将使用默认测试创建" ExampleInstrumentedTest.java"。
让我们其中编写一个测试:
package com.theitroad.androidexpressobasics; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; import static android.support.test.espresso.action.ViewActions.typeText; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.espresso.Espresso.onView; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.*; @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { public static final String USERNAME_TYPED = "Anupam"; public static final String LOGIN_TEXT = "Hello Anupam"; @Test public void useAppContext() { //Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.theitroad.androidexpressobasics", appContext.getPackageName()); } @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>( MainActivity.class); @Test public void loginClickedSuccess() { onView(withId(R.id.inUsername)) .perform(typeText(USERNAME_TYPED)); onView(withId(R.id.inNumber)) .perform(typeText("12345")); onView(withId(R.id.inConfirmNumber)) .perform(typeText("12345")); onView(withId(R.id.btnLogin)).perform(click()); onView(withId(R.id.txtLoginResult)).check(matches(withText(LOGIN_TEXT))); } }
@Rule批注用于启动MainActivity。
在" loginClickedSuccess"内部,我们编写了一个成功的登录测试用例。
现在再写两个测试,我们将测试Toast是否正确显示。
在上面的类中添加以下方法:
@Test public void shouldShowToastError() { onView(withId(R.id.inUsername)) .perform(typeText(USERNAME_TYPED)); onView(withId(R.id.inNumber)) .perform(typeText("123456")); onView(withId(R.id.inConfirmNumber)) .perform(typeText("12345"), closeSoftKeyboard()); onView(withId(R.id.btnLogin)).perform(click()); onView(withText(R.string.toast_error)).inRoot(withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).check(matches(isDisplayed())); } @Test public void shouldShowToastUsernameEmpty() { onView(withId(R.id.inNumber)) .perform(typeText("12345")); onView(withId(R.id.inConfirmNumber)) .perform(typeText("12345")); onView(withId(R.id.btnLogin)).perform(click()); onView(withText(R.string.username_empty)).inRoot(withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).check(matches(isDisplayed())); }
最后一行用于检查Toast是否显示正确的字符串。
运行Espresso测试。
右键单击ExampleInstrumentationTest.java,然后按运行以在模拟器上启动自动化测试。
以下是实际应用程序的输出:
以下是运行上述自动UI测试时控制台中的输出:
Espresso测试记录
您也可以手动记录Espresso测试:
它将使用Espresso测试自动创建一个新文件,其中包含您在设备中执行的手动单击和操作。