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我就放在下一篇博客吧!

0 0
原创粉丝点击