Android Espresso单元测试

Espresso介绍

在Android单元测试中,谷歌官方推荐使用Espresso框架,根据谷歌官方介绍,Espresso的最关键的优势就是它能自动同步模拟行为对UI的测试,它能够检测到主线程空闲状态的时候,以便在适当的时候运行你的测试代码或命令,这样你就没必要通过sleep去让主线程睡眠的方式去同步测试。说白了就是Espresso框架测试app不会通过阻塞主线程去同步UI测试。 
Espresso有三种重要体系的类,分别是Matchers(匹配器),ViewAction(界面行为),ViewAssertions(界面判断),其中Matchers是常常是通过匹配条件来需找UI组件或过滤UI,而ViewAction是来模拟用户操作界面的行为,ViewAssertions对模拟行为操作的View进行变换和结果验证,其三者关系如图所示: 
这里写图片描述
具体Espresso使用文档和Android 官方使用可以参考下面官方资料: 
https://google.github.io/android-testing-support-library/docs/index.html 
http://developer.android.com/training/testing/ui-testing/espresso-testing.html

Espresso实战

模拟用户行为测试: 
代码:

package com.scau.beyondboy.idgoods;

import android.support.test.espresso.contrib.PickerActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import android.widget.DatePicker;

import org.hamcrest.Matchers;
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.clearText;
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.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 2015-10-25
 * Time: 16:21
 */
//设置测试运行环境
@RunWith(AndroidJUnit4.class)
public class PersonInforTest
{
    private static final String TAG = PersonInforTest.class.getName();
    //设置启动的Activity
    @Rule
    public ActivityTestRule<MainActivity> mActivityTestRule=new ActivityTestRule<MainActivity>(MainActivity.class);
    //模拟用户的点击行为
    private void clicktest(final int id)
    {
        onView(withId(id)).perform(click());
    }
    //改变View的文本显示
    private String changetexttest(final int id,String text)
    {
        onView(withId(id)).perform(clearText(),typeText(text),closeSoftKeyboard());
        return text;
    }
    //检查View文本变化是否正确
    private void checktexttest(final int id,String text)
    {
        onView(withId(id)).check(matches(withText(text)));
    }

    //测试方法
   @Test
    public void NickNameTest()
   {
       clicktest(R.id.menu_toggle);
       clicktest(R.id.header_image);
       clicktest(R.id.nickname_layout);
       final String text=changetexttest(R.id.nickname,"test");
       clicktest(R.id.save);
       checktexttest(R.id.nickname, text);
   }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

运行结果: 
这里写图片描述 
测试成功会显示绿条: 
这里写图片描述
测试失败会显示红褐色条并报出对应的错误信息: 
这里写图片描述
Espresso异步测试: 
在开发中,我们经常会碰到异步请求,等异步请求完成后,再去更新UI,那么测试如何进行,估计很多测试新手可能会想到用Thread.sleep()方法让主线程睡眠等待,直到异步请求完成,刚开始我也是这么想的,但仔细想想这种方法是不对啊,我们知道主线程阻塞不能超过五秒,已超过五秒就会引起ANR异常,这种方式明显是不可行,那么有没有更好的方法呢?有,那就是通过Espresso的IdlingResource类来去完成,那是专门处理测试中的异步操作的发生,里面有两个相当重要的方法,其解释如下: 
public void registerIdleTransitionCallback(ResourceCallback callback); 这个方法注册回调。 
其回调接口:

 public interface ResourceCallback {
    /**
     * Called when the resource goes from busy to idle.
     */
    public void onTransitionToIdle();
  }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

public boolean isIdleNow(); 此方法通常用来通知主线程,其异步操作的完成,好让主线程更新UI,返回true便通知主线程去更新UI线程。 
代码:

package com.scau.beyondboy.idgoods;

import android.support.test.espresso.IdlingResource;

import com.scau.beyondboy.idgoods.view.SlideListView;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 2015-12-16
 * Time: 13:34
 * 实现ListView异步空闲处理类
 */
public class ListAdapterIdlingResource implements  IdlingResource
{
    private SlideListView mSlideListView;
    private IdlingResource.ResourceCallback mCallback;
    private final long startTime;
    private final long waitingTime;
    public  ListAdapterIdlingResource(long waitingTime,SlideListView slideListView)
    {
        this.startTime = System.currentTimeMillis();
        this.waitingTime = waitingTime;
        this.mSlideListView=slideListView;
    }

    @Override
    public String getName()
    {
        return "listadapterIdlingResource";
    }

    @Override
    public boolean isIdleNow()
    {
        //当网络数据加载完,才设置适配器,故可以通过适配器是否为空值来判断其异步数据加载是否完成
        if(mSlideListView.getAdapter()!=null)
        {
            mCallback.onTransitionToIdle();
            System.out.println("打印");
            return true;
        }
        return false;
        //通过时间来限制其异步加载
        /*long elapsed = System.currentTimeMillis() - startTime;
        boolean idle = (elapsed >= waitingTime);
        if (idle) {
            System.out.println("打印");
            mCallback.onTransitionToIdle();
        }
        return idle;*/
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback)
    {
        this.mCallback=callback;
    }
}


package com.scau.beyondboy.idgoods;

import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingPolicies;
import android.support.test.espresso.IdlingResource;
import android.support.test.espresso.matcher.BoundedMatcher;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;

import com.scau.beyondboy.idgoods.view.SlideListView;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.clearText;
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;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 2015-12-14
 * Time: 20:16
 */
@RunWith(AndroidJUnit4.class)
public class ProductTest
{
    private static final String TAG = ProductTest.class.getName();
    @Rule
    public ActivityTestRule<MainActivity> mActivityTestRule=new ActivityTestRule<MainActivity>(MainActivity.class);
    private void clicktest(final int id)
    {
        onView(withId(id)).perform(click());
    }

    private String changetexttest(final int id, String text)
    {
        onView(withId(id)).perform(clearText(),typeText(text),closeSoftKeyboard());
        return text;
    }

    private void checktexttest(final int id,String text)
    {
        onView(withId(id)).check(matches(withText(text)));
    }
    @Test    
    public void testClickOnItem()
    {
        clicktest(R.id.menu_toggle);
        clicktest(R.id.myproduct);
        int waitingTime=1000;
        //设置异步操作测试超时时间
        IdlingPolicies.setMasterPolicyTimeout(
                waitingTime * 10 TimeUnit.MILLISECONDS);
        IdlingPolicies.setIdlingResourceTimeout(
                waitingTime * 10, TimeUnit.MILLISECONDS);
        IdlingResource idlingResource=new ListAdapterIdlingResource(1000,(SlideListView)mActivityTestRule.getActivity().findViewById(R.id.product_slidelistview));
        //等待后台ListView加载完数据后执行后面的代码
        Espresso.registerIdlingResources(idlingResource);
        //选中一个listView的item选项           onData(anything()).inAdapterView(withId(R.id.product_slidelistview)).atPosition(10).perform(click());
        //释放对其异步空闲处理类
        Espresso.unregisterIdlingResources(idlingResource);
        clicktest(R.id.header_image);
        checktexttest(R.id.product_name, "其他的商品");
        pressBack();
        pressBack();        
    }    
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

运行结果如图: 
这里写图片描述 
Espresso Intent测试: 
代码:

package com.scau.beyondboy.idgoods;

import android.support.test.espresso.intent.Intents;
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.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.withId;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 2016-01-07
 * Time: 12:57
 * 第一种Intent测试
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class IntentTest
{
    @Rule
    public final ActivityTestRule<MainActivity> rule =
            new ActivityTestRule<>(MainActivity.class);   
    @Test
    public void intentTest()
    {
        //这种必须调用Intetns.init()方法
        Intents.init();
        clicktest(R.id.menu_toggle);
        clicktest(R.id.header_image);
        intended(hasComponent(PersonInfoActivity.class.getName()));
        clicktest(R.id.email_layout);
        intended(hasComponent(ChangeEmailActivity.class.getName()));
        Intents.release();
    }
    private void clicktest(final int id)
    {
        onView(withId(id)).perform(click());
    }
}




package com.scau.beyondboy.idgoods;

import android.support.test.espresso.intent.Intents;
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.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.withId;

/**
 * Author:beyondboy
 * Gmail:xuguoli.scau@gmail.com
 * Date: 2016-01-07
 * Time: 12:57
 * 第二种Intent测试
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class IntentTest
{    
    @Rule
   public IntentsTestRule<MainActivity> mRule = new IntentsTestRule<>(MainActivity.class);
    @Test
    public void intentTest()
    {       
        clicktest(R.id.menu_toggle);
        clicktest(R.id.header_image);
        intended(hasComponent(PersonInfoActivity.class.getName()));
        clicktest(R.id.email_layout);
        intended(hasComponent(ChangeEmailActivity.class.getName()));        
    }
    private void clicktest(final int id)
    {
        onView(withId(id)).perform(click());
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

运行结果: 
这里写图片描述 
参考资料: 
http://michaelevans.org/blog/2015/09/15/testing-intents-with-espresso-intents/ 
https://github.com/JakeWharton/double-espresso/tree/gradle/espresso-sample/src/androidTest/java/com/google/android/apps/common/testing/ui/testapp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值