Android测试系列之Instrumented Unit Test-Espresso
2016-02-28 14:39
417 查看
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. 检查测试效果
下面我们学习这两种自动化测试的时候的核心就是这两步,只有熟悉了这两种自动化测试实现的方式,以后的事情处理起来就的得心应手啦。
**
建立一个Espresso测试用例,有以下步骤:
1.利用@Rule注解方式指明要测试的Activity
2. 调用OnView()或者OnData()方法找到我们要测试的UI控件
3. 通过调用theViewInteraction.perform()或DataInteraction.perform()方法模拟一个用户操作
4. 使用ViewAssertions方法检查UI反映预期的状态或行为
下面就用两个简单的例子实现其测试功能:
第一个Demo:
Build.gradle
MainActivity.java
activity_main.xml
ShowTextActivity.java
activity_show_text.xml
叮叮叮当,我们的主角出场,测试用例
ChangeTextBehaviorTest.java
第二个Demo:
OtherActivity.java
activity_other.xml
叮叮叮当,主角出场(注意里面有hamcrest的应用,不了解的可以查查资料)
ListActivityTest.java
由于篇幅的原因,关于UiAutomator我就放在下一篇博客吧!
这是源于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) @LargeTest public 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) @LargeTest public 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我就放在下一篇博客吧!
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件