Android单元测试之对Activity的测试示例
上一篇文章已经介绍了单元测试的作用和简单示例,如果不了解的读者可以先阅读上一篇android单元测试-作用以及简单示例。
这篇文章主要介绍常见的activity中的测试。
对acitivity的测试
对于activity,我们大致有两种测试需求:
1、在activity正常启动后,查看界面布局是否正确,包括view的点击事件等是否正确。
2、需要在activity启动前完成各种数据的部署,然后查看activity的效果。
对于这两种需求,笔者分别做了两个示例解说:
1、检测一个布局中的button和textview是否正确。
2、从网络动态获取string到activity界面显示,并且这个图片的url是由intent传递过来的。
环境部署
首先要导入expresso-core的包,如下:
dependencies { // other dependencies ... androidtestcompile 'com.android.support.test.espresso:espresso-core:2.2.2' }
当然在目前的项目架构中一般已经自动导入了这个包,所以不需要自己导入,笔者项目中自动导入的包如下如下:
dependencies { compile filetree(include: ['*.jar'], dir: 'libs') androidtestcompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.0.0-alpha1' compile 'com.android.support.constraint:constraint-layout:1.0.2' testcompile 'junit:junit:4.12' }
项目结构如下:
布局view的测试:
package com.example.xujiajia_sx.myexpressotest; import android.app.activity; import android.os.bundle; import android.support.annotation.nullable; import android.view.view; import android.widget.button; import android.widget.textview; /** * created by xujiajia_sx on 2017/8/14. */ public class simpleviewactivity extends activity{ private textview tv; private button btn; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.act_simple_view); initview(); } private void initview() { tv=findviewbyid(r.id.tv_simple_view); btn=findviewbyid(r.id.btn_simple_view); tv.settext("111"); btn.settext("222"); btn.setonclicklistener(new view.onclicklistener() { @override public void onclick(view view) { tv.settext("777"); } }); } }
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <textview android:id="@+id/tv_simple_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <button android:id="@+id/btn_simple_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </linearlayout>
package com.example.xujiajia_sx.myexpressotest; import android.support.test.rule.activitytestrule; import org.junit.rule; import org.junit.test; 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.matches; import static android.support.test.espresso.matcher.viewmatchers.withid; import static android.support.test.espresso.matcher.viewmatchers.withtext; /** * created by xujiajia_sx on 2017/8/14. */ public class simpleviewtest { @rule public activitytestrule<simpleviewactivity> mactivitytestrule = new activitytestrule<simpleviewactivity>(simpleviewactivity.class); @test public void textviewtest() throws exception { onview(withid(r.id.tv_simple_view)) .check(matches(withtext("111"))); } @test public void buttontest() throws exception { onview(withid(r.id.btn_simple_view)) .check(matches(withtext("222"))) .perform(click()); onview(withid(r.id.tv_simple_view)) .check(matches(withtext("777"))); } }
测试主要逻辑:
1、首先要使用activitytestrule初始化你要测试的activity。
2、编写测试方法,测试view是否是我们预期的样子。
两个测试方法逻辑如下:
textviewtest():
在activity中查找id为tv_simple_view的view,检查它的text是否为“111”。
buttontest():
在activity中查找id为btn_simple_view的view,检查它的text是否为“222”。然后执行点击事件,点击事件的逻辑是在activity的oncreate中设置的,是把textview的text设置为777。在执行完点击事件后,测试方法中继续测试textview的text是否为“777”。
读者可能阅读到对view的测试非常陌生,不用担心,此处主要要理解测试的逻辑即可,笔者会在下篇文章具体讲解view的各种测试方法。
网络获取string的activity测试:
package com.example.xujiajia_sx.myexpressotest; import android.app.activity; import android.os.bundle; import android.support.annotation.nullable; import android.widget.textview; /** * created by xujiajia_sx on 2017/8/14. */ public class acttestactivity extends activity{ private textview tv; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.act_act_test); initview(); } private void initview() { tv= findviewbyid(r.id.tv_act_test); new thread(new runnable() { @override public void run() { string url =getintent().getstringextra("url"); final string s=mhttpclient.getinstance().get(url); runonuithread(new runnable() { @override public void run() { tv.settext(s); } }); } }).start(); } }
package com.example.xujiajia_sx.myexpressotest; /** * created by xujiajia_sx on 2017/8/14. */ public class mhttpclient { private static httpurlconnectionclient mclient = null; public static void setclient(httpurlconnectionclient client) { mclient = client; } public static httpurlconnectionclient getinstance() { return mclient; } }
package com.example.xujiajia_sx.myexpressotest; import android.util.log; import java.io.bufferedreader; import java.io.ioexception; import java.io.inputstream; import java.io.inputstreamreader; import java.net.httpurlconnection; import java.net.url; import static android.content.contentvalues.tag; /** * created by xujiajia_sx on 2017/8/14. */ public class httpurlconnectionclient { public string get(string url) { httpurlconnection conn = null; try { url murl = new url(url); conn = (httpurlconnection) murl.openconnection(); conn.setrequestmethod("get"); conn.setconnecttimeout(2000); conn.connect(); inputstream is = conn.getinputstream(); stringbuilder sb = new stringbuilder(); bufferedreader reader = new bufferedreader(new inputstreamreader(is)); string line; while ((line = reader.readline()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.tostring(); } catch (ioexception e) { log.e(tag, "network error for mini program ", e); return ""; } finally { //最后将conn断开连接 if (conn != null) { conn.disconnect(); } } } }
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <textview android:id="@+id/tv_act_test" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </linearlayout>
package com.example.xujiajia_sx.myexpressotest; import android.content.intent; import android.support.test.rule.activitytestrule; import org.junit.rule; import org.junit.test; /** * created by xujiajia_sx on 2017/8/14. */ public class acttest { @rule public activitytestrule<acttestactivity> mactivitytestrule= new activitytestrule<acttestactivity>(acttestactivity.class){ @override protected intent getactivityintent() { intent intent=new intent(); intent.putextra("url","http://www.weather.com.cn/adat/sk/101310201.html"); return intent; } @override protected void beforeactivitylaunched() { mhttpclient.setclient(new httpurlconnectionclient()); } }; @test public void mtest() throws exception{ thread.sleep(5000); } }
网络获取不要忘记在androidmanifest中加网络权限喔。
这个activity的主要逻辑就是接收intent,然后获取到传过来的url,接着通过网络获取到url的string,显示到textview上。
主要测试逻辑:
首先还是要定义activitytestrule,确定使用哪个activity。
与前一个例子不同的是,这里要重写activitytestrule的两个方法,getactivityintent() 和beforeactivitylaunched()。顾名思义,一个是设置activity获取到的intent,另一个是设置activity启动跟之前的准备工作。
笔者此处在getactivityintent() 中设置了传递的url,在beforeactivitylaunched()设置的网络获取的方式。
有些读者可能会好奇为什么网络获取的方式不默认呢,而要通过setclient()来设置?
因为这样可以更方便我们测试,在正式的项目中,我们可能会需要在代码中加入log等操作,但是正式的代码一般我们是不会去修改的,但是我们可以继承它,重写某些方法,然后把它放到测试需要的地方。
在这里我们就可以继承httpurlconnectionclient 这个类,然后把继承的子类使用setclient()来作为网络获取的方式。
总结
activity的使用方法大致如此了,如果有更多需求的读者可以查看一下官方activitytestrule的reference。
链接如下:https://developer.android.google.cn/reference/android/support/test/rule/activitytestrule.html
第一种使用方法中设计到了对view的测试,由于篇幅较大,本篇文章未能详细讲述,笔者会在下篇文章做一定讲解。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。