欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

简单谈谈android studio 的单元测试

程序员文章站 2024-03-06 10:57:49
面对android studio run 一次项目要等好几分钟的痛点,不得不研究一下android studio 的单元测试。 其实我的目的很简单,在不对视图进行操作...

面对android studio run 一次项目要等好几分钟的痛点,不得不研究一下android studio 的单元测试。

其实我的目的很简单,在不对视图进行操作的前提下,测试一些activity 的生命周期,或网络拉取数据的一些处理,比如解析 json 数据啊,做网络请求啊等等,也就是对 model层的测试。这些不需要操作视图,但在没有单元测试环境下,比如我们网络请求一些数据,log 打印看看是否请求成功,却又要 利用模拟器或真机run 一次项目,花费好几分钟,这是不能容忍的。

于是乎,强大的 android studio 也考虑到了这一点,给我们提供的简单的单元测试类。

让我们来简单的了解学习一下吧。

首先先来了解一下一些名称,方便下面介绍和使用:

在java中咱们有用过 junit 的 单元测试 ,那android 也是基于 java 语言编写的,所以也有个 junit的单元测试。在做 android 的单元测试需要导入依赖:

androidtestcompile 'junit:junit:4.12'
testcompile 'junit:junit:4.12'

其中, test目录为在本机执行单元测试代码的目录, androidtest为在android设备上执行单元测试代码的目录。如下图:

简单谈谈android studio 的单元测试

android 自带的 junit单元测试的一些测试类(androidtest测试 需要运行在模拟机或真机上)

1、instrumentationtestcase框架:

instrumentation和activity有点类似,只不过activity是需要一个界面的,而instrumentation并不是这样的,我们可以将它理解为一种没有图形界面的,具有启动能力的,用于监控其他类(用target package声明)的工具类。

举个例子,利用instrumentationtestcase 启动一个activity:

在androidtest下新建一个java类,并且继承自instrumentationtestcase编写一个public void的方法,但是必须要是方法名以test打头,比如testpublishsubject,并不需要@test注解

public class testsubject extends instrumentationtestcase {
  private static final string log_tag = "test";
 
  public void testpublishsubject() {
 
    launchactivity("demo.zts.com.demo",secondactivity.class,null);
  }
}

2、applicationtestcase——测试整个应用程序的类。它允许你注入一个模拟的context到应用程序中,在应用程序启动之前初始化测试参数,并在应用程序结束之后销毁之前检查应用程序。

使用context,你可以浏览资源,文件,数据库等等。基类是androidtestcase,一般常见的是它的子类,和特定组件关联。

测试代码如下:

public class myapp extends application {
  @override
  public void oncreate() {
    super.oncreate();
    string app_name = getresources().getstring(r.string.app_name);
    log.i("myapp",".........myapp....app_name.........."+app_name);
  }
}
public class applicationtest extends applicationtestcase<myapp> {
  public applicationtest() {
    super(myapp.class);
  }
  public void teststart() {
    string str = null;
    str = mcontext.getresources().getstring(r.string.app_name);
    log.i("..",".............applicationtest ...........app_name............."+str);
  }

log 日志:

 07-22 23:27:10.276 32259-32259/demo.zts.com.demo i/myapp: .........myapp....app_name..........demo
 07-22 23:27:10.276 32259-32319/demo.zts.com.demo i/testrunner: started: teststart(demo.zts.com.demo.applicationtest)
 07-22 23:27:10.286 32259-32319/demo.zts.com.demo i/..: .............applicationtest..........app_name..............demo

3、activityunittestcase——对单个activity进行单一测试的类。使用它,你可以注入模拟的context或application,或者两者。它用于对activity进行单元测试。也就是说你可以用于测试单独的activity ,虽然也需要利用模拟机或真机启动,但你启动的只是你需要做测试的activity,于其他activity无关。
测试代码如下:

要测试的 activity

public class mainactivity extends appcompatactivity {
  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    system.out.println("...............mainactivity......oncreate............");
    log.i("mainactivity","................oncreate............................");

  }
  @override
  protected void onstart() {
    super.onstart();
    system.out.println("...............mainactivity......onstart............");
    log.i("mainactivity","................onstart............................");
  }
  @override
  protected void onstop() {
    super.onstop();
    system.out.println("...............mainactivity......onstop............");
    log.i("mainactivity","................onstop............................");
  }
  @override
  protected void ondestroy() {
    super.ondestroy();
    system.out.println("...............mainactivity......ondestroy............");
    log.i("mainactivity","................ondestroy............................");
  }
}

测试类

public class testactivity extends activityinstrumentationtestcase2<mainactivity> {

  private context ctx;

  public testactivity() {
    super(mainactivity.class);
  }

  @override
  protected void setup() throws exception {
    super.setup();
    ctx = getactivity().getapplicationcontext();
  }

  public void teststart() {
    intent intent = new intent(ctx, mainactivity.class);
    intent.setflags(intent.flag_activity_new_task);
    ctx.startactivity(intent);
    log.i("testactivity","................startactivity............................");

  }

测试 log 日志:

  * 07-22 23:39:44.146 3171-3171/demo.zts.com.demo i/system.out: ...............mainactivity......oncreate............
     07-22 23:39:44.146 3171-3171/demo.zts.com.demo i/mainactivity: ................oncreate............................
     07-22 23:39:44.151 3171-3171/demo.zts.com.demo d/mzperfobserver: demo.zts.com.demo oncreate consume 153 ms
     07-22 23:39:44.151 3171-3171/demo.zts.com.demo i/system.out: ...............mainactivity......onstart............
     07-22 23:39:44.151 3171-3171/demo.zts.com.demo i/mainactivity: ................onstart............................
     07-22 23:39:44.326 3171-3171/demo.zts.com.demo d/openglrenderer: enabling debug mode 0
     07-22 23:39:44.361 3171-3171/demo.zts.com.demo i/system.out: ...............mainactivity......onstop............
     07-22 23:39:44.361 3171-3171/demo.zts.com.demo i/mainactivity: ................onstop............................
 
     07-22 23:39:44.421 3171-3224/demo.zts.com.demo i/testactivity: ................startactivity............................

还有很多常见的测试,比如servicetestcase,providertestcase2等,大家需要慢慢琢磨。

android 自带的 junit单元测试的一些测试类(test 测试 ,不需要模拟机,电脑直接运行)

比如我需要测试一段java代码,而这段java代码跟android没关系,也就是不用到android的资源,如context,activity 等,说白了就是简单的 java 测试,当然,嘿嘿,android studio也是可以做java代码测试的。

测试代码如下,测试 4+4 等于几:

public class exampleunittest {
  @test
  public void testadd() {

    int i = 0;
    i = 4+4;
    system.out.print(".............. "+i);
    log.i("tag","..................."+i);
    // 比较 i 是否 等于 8 ,相等的话通过测试!!!
    assert.assertequals(8, i);
  }
}

测试成功:

简单谈谈android studio 的单元测试

以上测试类的运行是 -点击测试右键 - 选择 runxxxxx

简单谈谈android studio 的单元测试

/*********************华丽分割线***********************/

看了半天好像也没有解决文章最初提到的一个痛点啊,就是我需要测试android的资源,但又不想运行笨重的模拟机或真机,怎么办呢? 妈蛋,被骗了,还钱 -_-、、、 确实,上面提到的测试方法虽然没有解决拜托模拟机测试的痛点,但基于模拟机单元测试的 androidtest 确实方便我们做一些 单独功能的测试,而且能做 ui 测试,因为需要模拟机或真机嘛,所以 ui 或视图测试是没问题的。 还有test 测试,可以做一些不需要android资源的 java代码测试,也是在android开发当中很方便的,不用在启用eclipse 做测试,直接android studio 既可以了。

忽悠,接着 忽悠 -_-////

其实要想脱离 模拟机或真机,又要做使用android资源的测试,如 使用context,浏览资源,文件,数据库等等。 也是可以的!!! 那 就只有第三方测试框架了 robolectric

666,你是来做宣传的吗 -_-、、、不过真的很好用,也能很好的解决咱们的痛点。

接下来利用个需求来讲解 robolectric 测试,免得我忽悠你们。

拿到 android 目录下的 assets 下的json01.txt文件 是一段json数据,让后进行解析,解析后将数据显示。 分析:这个需求就跟android下的资源有关,而咱们利用 robolectric 做单元测试,并且不需要模拟机或真机的支持。

简单谈谈android studio 的单元测试

其中json数据

{
 "name": "coolxing",
 "age": 24,
 "male": true,
 "address": {
  "street": "huilongguan",
  "city": "beijing",
  "country": "china"
 }
}

首先需要 robolectric 依赖,在你的 app module 下注入依赖:

testcompile 'org.robolectric:robolectric:3.0'
注意是 testcompile 而不是 androidtestcompile ,不然你有需要启动模拟器了。并且测试类也是 在 test 下的

简单谈谈android studio 的单元测试

测试类:

@runwith(robolectricgradletestrunner.class)
@config(constants = buildconfig.class, sdk = 21)
public class mainactivitytest2 {

  @test
  public void testjson(){
    string str = null;
    str = runtimeenvironment.application.getresources().getstring(r.string.app_name);

    assetmanager am = null;
    am = runtimeenvironment.application.getassets();
    string strdata = null;
    try {
      inputstream inputstream = am.open("json01.txt");
      byte buf[] = new byte[1024];
      inputstream.read(buf);
      strdata = new string(buf);
      strdata =strdata.trim();
      strdata.trim();
    } catch (ioexception e) {

    }
    jsonbean foo = new gson().fromjson(strdata, jsonbean.class);
    system.out.println("...............json.................."+foo.name);
    system.out.println("...............json.................."+foo.address);
    system.out.println("...............json.................."+foo.age);
  }
}

测试结果:

简单谈谈android studio 的单元测试

看,咱们利用application 拿到 android 下的资源,但又不像刚才上面的 androidtestcompile 需要模拟机,是不是很6,我电脑配置比较低,本次测试需要40s多,但不真机快多了。

am = runtimeenvironment.application.getassets();

需要注意几点,类头部需要声明  @ 注解:

@runwith(robolectricgradletestrunner.class)
@config(constants = buildconfig.class, sdk = 21)

并且测试方法是以 textxxx() 开头的,如上面的 testjson() ,方法也需要@test注解!!!
robolectric 还可以测试 activity ,如:

@runwith(robolectricgradletestrunner.class)
@config(constants = buildconfig.class, sdk = 21)
public class mainactivitytest2 {
  @test
  public void testmainactivity() {

    mainactivity mainactivity = robolectric.setupactivity(mainactivity.class);
    mainactivity.findviewbyid(r.id.main_tv).performclick();


    intent expectedintent = new intent(mainactivity, secondactivity.class);

    shadowactivity openactivity = shadows.shadowof(mainactivity);
    intent actualintent = openactivity .getnextstartedactivity();

    // assert.assertequals(expectedintent, actualintent);

  }

其中

mainactivity mainactivity = robolectric.setupactivity(mainactivity.class);

这句代码就是启动了mainactivity 的生命周期

简单谈谈android studio 的单元测试

robolectric 单元测试类 的 启动 也是跟 上面test 测试类一样,选择 -mainactivitytest2 --右键 -- 选择 run mainactivitytest2

好了,单元测试就介绍到这里,

其实我也只是初步理解,上面那些基本的也是我做项目的需要我才去学习使用的,还有好多强大的功能大家慢慢探索。