Android测试系列之Instrumented Unit Test-Espresso
来源:互联网 发布:90后流行网络女歌手 编辑:程序博客网 时间:2024/06/05 21:00
Instrumented unit tests are unit tests that run on physical devices and emulators, instead of the Java Virtual Machine (JVM) on your local machine.
这是源于Google API文档的一句话,它告诉我们Instrument Unit Test区别于Unit Test的地方就是它能够在物理设备和虚拟机上运行。所以我们多来进行自动化的UI测试。
注意两点:
第一、以下所有关于Instrumented Unit Test讲解的测试环境都是在Android Studio
第二、android studio测试代码运行的Build Variants的Test ArtiFact的模式都是Android Instrumentation Test。
Google推荐了两种自动化UI测试的方式,第一种是利用Espresso,这种方式适用于单个应用程序的测,测试环境是android 2.2(API 8)及其以上;第二种是UiAutoMator,这种方式适用于多个应用程序的测试,测试环境是android 4.3(API 18)及其以上。
在自动化测试的测试中:
1. 找到我们要测试的对象
2. 测试对象的动作
3. 检查测试效果
下面我们学习这两种自动化测试的时候的核心就是这两步,只有熟悉了这两种自动化测试实现的方式,以后的事情处理起来就的得心应手啦。
**
Testing UI for a Single App(Espresso)
**
建立一个Espresso测试用例,有以下步骤:
1.利用@Rule注解方式指明要测试的Activity
2. 调用OnView()或者OnData()方法找到我们要测试的UI控件
3. 通过调用theViewInteraction.perform()或DataInteraction.perform()方法模拟一个用户操作
4. 使用ViewAssertions方法检查UI反映预期的状态或行为
下面就用两个简单的例子实现其测试功能:
第一个Demo:
Build.gradle
apply plugin: 'com.android.application'android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.example.myapplication" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } lintOptions { abortOnError false } productFlavors { } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { // App dependencies compile 'com.android.support:support-annotations:23.0.1' compile 'com.google.guava:guava:18.0' // Testing-only dependencies // Force usage of support annotations in the test app, since it is internally used by the runner module. androidTestCompile 'com.android.support:support-annotations:23.0.1' androidTestCompile 'com.android.support.test:runner:0.4.1' androidTestCompile 'com.android.support.test:rules:0.4.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1' testCompile 'junit:junit:4.12'}
MainActivity.java
import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends Activity implements View.OnClickListener { // The TextView used to display the message inside the Activity. private TextView mTextView; // The EditText where the user types the message. private EditText mEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Set the listeners for the buttons. findViewById(R.id.changeTextBt).setOnClickListener(this); findViewById(R.id.activityChangeTextBtn).setOnClickListener(this); mTextView = (TextView) findViewById(R.id.textToBeChanged); mEditText = (EditText) findViewById(R.id.editTextUserInput); } @Override public void onClick(View view) { // Get the text from the EditText view. final String text = mEditText.getText().toString(); switch (view.getId()) { case R.id.changeTextBt: // First button's interaction: set a text in a text view. mTextView.setText(text); break; case R.id.activityChangeTextBtn: // Second button's interaction: start an activity and send a message to it. Intent intent = ShowTextActivity.newStartIntent(this, text); startActivity(intent); break; } }}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/activity_horizontal_margin" tools:context=".MainActivity"> <TextView android:id="@+id/textToBeChanged" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="@dimen/header_margin" android:layout_marginTop="@dimen/header_margin" android:text="@string/hello_world" android:textAppearance="?android:attr/textAppearanceLarge"/> <EditText android:id="@+id/editTextUserInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:hint="@string/type_something"/> <Button style="?android:attr/buttonStyleSmall" android:id="@+id/changeTextBt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/change_text" android:layout_gravity="center_horizontal"/> <Button style="?android:attr/buttonStyleSmall" android:id="@+id/activityChangeTextBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/open_activity_and_change_text"/></LinearLayout>
ShowTextActivity.java
import android.app.Activity;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.widget.TextView;import com.google.common.base.Strings;/** * Created by Administrator on 16-2-23. */public class ShowTextActivity extends Activity { // The name of the extra data sent through an {@link Intent}. public final static String KEY_EXTRA_MESSAGE = "com.example.myapplication.MESSAGE"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_text); // Get the message from the Intent. Intent intent = getIntent(); String message = Strings.nullToEmpty(intent.getStringExtra(KEY_EXTRA_MESSAGE)); // Show message. ((TextView)findViewById(R.id.show_text_view)).setText(message); } /** * Creates an {@link Intent} for {@link ShowTextActivity} with the message to be displayed. * @param context the {@link Context} where the {@link Intent} will be used * @param message a {@link String} with text to be displayed * @return an {@link Intent} used to start {@link ShowTextActivity} */ static protected Intent newStartIntent(Context context, String message) { Intent newIntent = new Intent(context, ShowTextActivity.class); newIntent.putExtra(KEY_EXTRA_MESSAGE, message); return newIntent; }}
activity_show_text.xml
<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ShowTextActivity"> <TextView android:id="@+id/show_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" android:layout_gravity="center"/></merge>
叮叮叮当,我们的主角出场,测试用例
ChangeTextBehaviorTest.java
import android.support.test.rule.ActivityTestRule;import android.support.test.runner.AndroidJUnit4;import android.test.suitebuilder.annotation.LargeTest;import org.junit.Rule;import org.junit.Test;import org.junit.runner.RunWith;import static android.support.test.espresso.Espresso.onView;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.ViewMatchers.withId;import static android.support.test.espresso.matcher.ViewMatchers.withText;/** * Created by Administrator on 16-2-23. */@RunWith(AndroidJUnit4.class)@LargeTestpublic class ChangeTextBehaviorTest { public static final String STRING_TO_BE_TYPED = "espresso"; @Rule public ActivityTestRule<MainActivity> mActivityRule=new ActivityTestRule<MainActivity>(MainActivity.class); @Test public void changeText_sameActivity(){ // Type text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(STRING_TO_BE_TYPED),closeSoftKeyboard()); onView(withId(R.id.changeTextBt)) .perform(click()); // Check that the text was changed. onView(withId(R.id.textToBeChanged)) .check(matches(withText(STRING_TO_BE_TYPED))); } @Test public void changeText_newActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)).perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); onView(withId(R.id.activityChangeTextBtn)).perform(click()); // This view is in a different Activity, no need to tell Espresso. onView(withId(R.id.show_text_view)).check(matches(withText(STRING_TO_BE_TYPED))); }}
第二个Demo:
OtherActivity.java
import android.app.Activity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class OtherActivity extends Activity { private List<Map<String,Object>> datas; private MyAdapter myAdapter; private ListView myListview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_other); initView(); initData(); setAdapter(); } private void initView(){ myListview = (ListView) findViewById(R.id.my_lv); } private void initData(){ datas = new ArrayList<>(); for (int i = 0; i < 15; i++) { Map<String,Object> map = new HashMap<>(); map.put("number","测试数据"+i); datas.add(map); } } private void setAdapter(){ myAdapter = new MyAdapter(); myListview.setAdapter(myAdapter); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return datas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView==null){ holder = new ViewHolder(); convertView= LayoutInflater.from(OtherActivity.this).inflate(R.layout.list_item,null); holder.tv = (TextView) convertView.findViewById(R.id.rowContentTextView); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.tv.setText(datas.get(position).get("number").toString()); return convertView; } final class ViewHolder{ TextView tv; } }}
activity_other.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/my_lv" android:layout_width="match_parent" android:layout_height="match_parent"></ListView></RelativeLayout>
叮叮叮当,主角出场(注意里面有hamcrest的应用,不了解的可以查查资料)
ListActivityTest.java
import org.junit.Before;import org.junit.Rule;import org.junit.Test;import org.junit.runner.RunWith;import android.support.test.espresso.DataInteraction;import android.support.test.espresso.Espresso;import android.support.test.espresso.action.ViewActions;import android.support.test.espresso.matcher.ViewMatchers;import android.support.test.rule.ActivityTestRule;import android.support.test.runner.AndroidJUnit4;import android.test.ActivityInstrumentationTestCase2;import android.test.suitebuilder.annotation.LargeTest;import java.util.Map;import static android.support.test.espresso.Espresso.onData;import static android.support.test.espresso.Espresso.onView;import static android.support.test.espresso.action.ViewActions.click;import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;import static android.support.test.espresso.assertion.ViewAssertions.matches;import static android.support.test.espresso.matcher.ViewMatchers.isChecked;import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;import static android.support.test.espresso.matcher.ViewMatchers.withId;import static android.support.test.espresso.matcher.ViewMatchers.withText;import static org.hamcrest.Matchers.allOf;import static org.hamcrest.Matchers.equalTo;import static org.hamcrest.Matchers.hasEntry;import static org.hamcrest.Matchers.instanceOf;import static org.hamcrest.Matchers.is;/** * Created by Administrator on 16-2-25. */@RunWith(AndroidJUnit4.class)@LargeTestpublic class LongListActivityTest { private static final String TEXT_ITEM_30 = "item: 30"; private static final String TEXT_ITEM_30_SELECTED = "30"; private static final String TEXT_ITEM_60 = "item: 60"; // Match the last item by matching its text. private static final String LAST_ITEM_ID = "测试数据14"; /** * A JUnit {@link Rule @Rule} to launch your activity under test. This is a replacement * for {@link ActivityInstrumentationTestCase2}. * <p> * Rules are interceptors which are executed for each test method and will run before * any of your setup code in the {@link Before @Before} method. * <p> * {@link ActivityTestRule} will create and launch of the activity for you and also expose * the activity under test. To get a reference to the activity you can use * the {@link ActivityTestRule#getActivity()} method. */ @Rule public ActivityTestRule<OtherActivity> mActivityRule = new ActivityTestRule<>( OtherActivity.class); /** * Test that the list is long enough for this sample, the last item shouldn't appear. */ @Test public void lastItem_NotDisplayed() { // Last item should not exist if the list wasn't scrolled down. onView(withText(LAST_ITEM_ID)).check(doesNotExist()); } /** * Check that the item is created. onData() takes care of scrolling. */ @Test public void list_Scrolls() { onRow(LAST_ITEM_ID).check(matches(isCompletelyDisplayed())); } /** * Clicks on a row and checks that the activity detected the click. */ @Test public void row_Click() { // Click on one of the rows. onRow(LAST_ITEM_ID).onChildView(withId(R.id.rowToggleButton)).perform(click()); // Check that the activity detected the click on the first column. /* onView(ViewMatchers.withId(R.id.selection_row_value)) .check(matches(withText(TEXT_ITEM_30_SELECTED)));*/ } /** * Checks that a toggle button is checked after clicking on it. */ @Test public void toggle_Click() { // Click on a toggle button. onRow(TEXT_ITEM_30).onChildView(withId(R.id.rowToggleButton)).perform(click()); // Check that the toggle button is checked. onRow(TEXT_ITEM_30).onChildView(withId(R.id.rowToggleButton)).check(matches(isChecked())); } /** * Make sure that clicking on the toggle button doesn't trigger a click on the row. */ @Test public void toggle_ClickDoesntPropagate() { // Click on one of the rows. onRow(TEXT_ITEM_30).onChildView(withId(R.id.rowContentTextView)).perform(click()); // Click on the toggle button, in a different row. onRow(TEXT_ITEM_60).onChildView(withId(R.id.rowToggleButton)).perform(click()); // Check that the activity didn't detect the click on the first column. onView(ViewMatchers.withId(R.id.selection_row_value)) .check(matches(withText(TEXT_ITEM_30_SELECTED))); } /** * Uses {@link Espresso#onData(org.hamcrest.Matcher)} to get a reference to a specific row. * <p> * Note: A custom matcher can be used to match the content and have more readable code. * See the Custom Matcher Sample. * </p> * * @param str the content of the field * @return a {@link DataInteraction} referencing the row */ private static DataInteraction onRow(String str) { return onData(hasEntry("number", str)); }}
由于篇幅的原因,关于UiAutomator我就放在下一篇博客吧!
- Android测试系列之Instrumented Unit Test-Espresso
- Android测试系列之Instrumented Unit Test-UiAutomator
- Android测试系列之Local Unit Test
- 使用Espresso Test Recorder编写Android测试
- Android测试之设备化测试(Instrumented Tests)
- Android测试详解_3-Building Instrumented Unit Tests-创建仪器单元测试
- Android测试之本地单元测试(Local Unit Test)
- 在Cordova中自定义AndroidTest(Instrumented Unit Test)
- Android Espresso Test UI
- Android instrumented test no tests found
- Android UI测试之Espresso使用
- Espresso之 list测试
- Android测试 ---- Espresso + Jacoco
- Android Espresso UI测试
- Android Espresso测试
- Android 系列 3.6使用ATSL,Espresso和JUnit测试4
- Android Unit Test
- android unit test
- Android内存管理原理
- java中 i++ 与 ++i
- nyoj801 哈夫曼编码,输入有毒
- Android的内存机制和常见泄漏情形
- uva10791 Minimum Sum LCM
- Android测试系列之Instrumented Unit Test-Espresso
- CSU 1604: SunnyPig
- 国务院关于建立城镇职工基本医疗保险制度的决定
- poj3682 概率
- tcpdump&pt-query-digest分析mysql负载性能问题
- 个人学习-java-final关键字
- c++ gui qt4 chap02 gotocell
- bfc与自适应笔记
- 黑格尔经典名言