Robolectric通过实现一套JVM能运行的Android代码,然后在Junit test运行的时候去截取android相关的代码调用,然后转到他们的他们实现的代码去执行这个调用的过程。不用依赖真实的 Android 环境中运行(模拟器或者真机)

Robolectric主要适用于UI的测试,比如Activity,Fragment,一些页面操作的测试场景,采用Shadow的方式对Android中的组件进行模拟测试,从而实现Android单元测试Robolectric正好弥补了Mockito的不足,两者结合使用是最完美的。

现在我们就开启Robolectric的测试之旅吧!

需要测试的类:

public class QXUnitTestActvity extends MSBaseActivity {
@BindView(R.id.btn_changge) Button mbtn;
@BindView(R.id.textView) TextView mTv;
public String name;
private QxBroacastReceiver receiver;
private Handler mHandler;
public int mCount=0;

@Override
public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_unit_test);ButterKnife.bind(this);name="create";registerBroacast();mHandler=new Handler(getMainLooper());}private void registerBroacast() {IntentFilter filter=new IntentFilter();filter.addAction("com.wr.qx.ok");receiver= new QxBroacastReceiver();registerReceiver(receiver,filter);
}@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@OnClick(R.id.btn_changge) void changeText(){mTv.setText("我被修改了");if(name.equals("jump")){startActivity(new Intent(this,MSCurrentInvestActivity.class));}mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mCount=6;}},500);Toast.makeText(this,"hello",Toast.LENGTH_LONG).show();Dialog dialog=new Dialog(this);TextView tv=new TextView(this);tv.setText("我是一个dialog");dialog.setContentView(tv);dialog.show();dialog.setCancelable(true);final AlertDialog alertDialog=new AlertDialog.Builder(this).setIcon(getDrawable(R.mipmap.ic_launcher)).setMessage("hello").setNegativeButton("OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {dialogInterface.dismiss();}}).create();alertDialog.show();
}@Override
protected void onStart() {super.onStart();name="start";
}@Override
protected void onResume() {super.onResume();name="jump";
}@Override
protected void onPause() {super.onPause();name="pause";
}@Override
protected void onStop() {super.onStop();name="stop";
}@Override
protected void onDestroy() {super.onDestroy();name="destroy";unregisterReceiver(receiver);
}@Override
public void onBackPressed() {super.onBackPressed();name="onBackPressed";
}

}

测试类:

@RunWith(RobolectricTestRunner.class)//表示用RobolectricTestRunner来跑测试
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)//基本的配置信息

public class QXUnitTestActvityTest {
private QXUnitTestActvity mActvity;
private Button mbtn;
private TextView mTv;
private CountDownLatch runFlag;

@Before
public void setUp() {//加上这么一句话无论是测试代码中的log或者被测试中的log都会在控制台输出的ShadowLog.stream = System.out;Log.d("Test_log", "开启了log日志");//相当于启动了actvity 经过了 oncreate,onStart和onResume这三个生命周期//一般只是在启动activiy的时候调用mActvity = Robolectric.setupActivity(QXUnitTestActvity.class);mbtn = (Button) mActvity.findViewById(R.id.btn_changge);mTv = (TextView) mActvity.findViewById(R.id.textView);
}@After
public void tearDown() {mActvity = null;mbtn = null;mTv = null;
}//测试启动初始化是否成功
@Test
public void testInit() {Assert.assertEquals("actvity packageName is error", "com.ran.qxlinechart", mActvity.getPackageName());Assert.assertNotNull("button is null", mbtn);Assert.assertNotNull("testView is null", mTv);Assert.assertEquals("textView text is error", "text", mTv.getText().toString());
}//测试点击事件是否相应
@Test
public void testClickButton() {mbtn.performClick();Assert.assertEquals("textView text is error", "我被修改了", mTv.getText().toString());
}//测试Actvity生命周期的调用
@Test
public void testLifecycleMethods() throws InterruptedException {ActivityController<QXUnitTestActvity> controller = Robolectric.buildActivity(QXUnitTestActvity.class);QXUnitTestActvity actvity = (QXUnitTestActvity) controller.get();//所有的生命周期都可以模仿但是这里的actviy一定是controller里get得到的,// 不能用mActvity因为全局的那个是tartUp的更生命周期的这个不是一个controller.create();Assert.assertEquals("create", actvity.name);controller.start();Assert.assertEquals("start", actvity.name);controller.resume();Assert.assertEquals("jump", actvity.name);controller.pause();Assert.assertEquals("pause", actvity.name);controller.stop();Assert.assertEquals("stop", actvity.name);controller.destroy();Assert.assertEquals("destroy", actvity.name);mbtn.performClick();Assert.assertEquals("textView text is error", "我被修改了", mTv.getText().toString());}//Fragment是继承app.Fragment 生命周期
@Test
public void testFragmentLifecyle() {FragmentController<QxFramentApp> controller = Robolectric.buildFragment(QxFramentApp.class);QxFramentApp framentApp = controller.get();controller.create();这样一步就走到了onCreatView不仅仅走了onCraeteAssert.assertEquals("createView", framentApp.name);controller.start();Assert.assertEquals("start", framentApp.name);controller.resume();Assert.assertEquals("resume", framentApp.name);controller.destroy();Assert.assertEquals("destroy", framentApp.name);}//测试Actvity的跳转
@Test
public void testStartActiviy() {mbtn.performClick();Assert.assertEquals("textView text is error", "我被修改了", mTv.getText().toString());

// mActvity.name=”jump”; 使用这个是错误的

// ActivityController controller=Robolectric.buildActivity(QXUnitTestActvity.class).create().start();//已经走到resume了
//
// QXUnitTestActvity actvity= (QXUnitTestActvity) controller.get();
// actvity.name=”jump”;也是行不通的
//只能在自己的生命周期里更改name
ShadowApplication shadowApplication = ShadowApplication.getInstance();
//表示shadowApplication.getNextStartedActivity()不能是null(notNullValue())如果是null就会报错,然后提示”next actvity is null shoud not jump”
// Assert.assertThat(“next actvity is null shoud not jump”,shadowApplication.getNextStartedActivity(),is(notNullValue()));

    //或许用下面这个来验证 用上面注释的一条验证最简单Intent expIntent = new Intent(mActvity, MSCurrentInvestActivity.class);Intent act = shadowApplication.getNextStartedActivity();

//如果两者不同就会报错提示
Assert.assertEquals(“不是从mActvity跳转到MSCurrentInvestActivity或者是携带的数据不一样”, expIntent.getComponent(), act.getComponent());
}

//Fragment是继承v4的 测试Fragment添加初始化是否成功
@Test
public void testFragment() {QxFragment fragment = new QxFragment();//把Fragment添加到Activity中,并且启动Fragment,触发他的生命周期函数OnCreateViewSupportFragmentTestUtil.startFragment(fragment);Bundle bundle = fragment.getArguments();Assert.assertThat("fragment not start success", fragment.getView(), is(notNullValue()));
}//测试模拟延迟任务的执行
@Test
public void testDelayTask() {Assert.assertFalse(mActvity.mCount == 6);//此时没有走线程呢所以不会是6mbtn.performClick();//利用Looper的影子来安排任务的执行,这也符合Android的架构情况ShadowLooper.runUiThreadTasksIncludingDelayedTasks();//等待UI delay的Tasks运行完毕然后再往下执行Assert.assertTrue(mActvity.mCount == 6);
}/*** ShadowXX其实就是这个XX的影子,代理或者是复制品  有N个ShadowXX,比如ShadowApplication,ShadowActvity,ShadowToast* ShadowListView,ShadowLinearLayout,ShadowBitmap等,也就是说只要Android有的类几乎都有相应的影子Shadow。也就是说只要我们* 有了一个类的Shadow我们就相当于有了这个类,而且功能更强大,毕竟是经过了,包装的了,创建一个类的Shadow也很容易:* ShadowActivity shadowActivity= Shadows.shadowOf(mActvity);* View vie= shadowActivity.getContentView();*/
@Test
public void testBroadCast() {ShadowApplication shadowApplication = ShadowApplication.getInstance();Intent intent = new Intent("com.wr.qx.ok");Assert.assertTrue(shadowApplication.hasReceiverForIntent(intent));//是否已经注册action是"com.wr.qx.ok"的广播//模拟注册广播后 激发广播后的操作QxBroacastReceiver receiver = new QxBroacastReceiver();receiver.onReceive(RuntimeEnvironment.application, intent);//检查接到广播后是否启动了context.startActivity(new Intent(context, MSCurrentInvestActivity.class));Assert.assertThat("没有收到广播 然后启动actvity", shadowApplication.getNextStartedActivity(), is(notNullValue()));
}@Test
public void testDialog() {mbtn.performClick();Dialog dialog = ShadowDialog.getLatestDialog();Assert.assertNotNull("dialog not creat", dialog);dialog.dismiss();Assert.assertTrue(!dialog.isShowing());
}@Test
public void testAlertDialog() {mbtn.performClick();AlertDialog alertDialog = ShadowAlertDialog.getLatestAlertDialog();Assert.assertNotNull("dialog not creat", alertDialog);alertDialog.dismiss();Assert.assertTrue(!alertDialog.isShowing());}@Test
public void testToast() {mbtn.performClick();Assert.assertNotNull("not invote Toast", ShadowToast.getLatestToast());String toastText = ShadowToast.getTextOfLatestToast();Assert.assertEquals("toast error", "hello", toastText);
}//测试模拟资源文件的获取
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Test
public void testReadResouce() {Application application = RuntimeEnvironment.application;Drawable drawable = application.getDrawable(R.mipmap.ic_launcher);
}

}

综上:Robolectric的之所以能灵活的操作UI,Actvity,Fragment,Resouce,Application等几乎所有Android API中的基础类,原因就是Robolectric内部把这些分别包装成了,各自的影子类ShadowXX,然后利用这些影子类模拟原类的行为和属性,从而达到测试的目的,比如上面我们用到的ShadowApplication ,ShadowActivity,ShadowDialog等,还有就是Android的几个核心类对应的XXController比如ActivityController和FragmentController等来控制自己的生命周期方法和主要行为。

单元测试Robolectric的使用详解相关推荐

  1. JUnit4单元测试、JUnit4使用详解、assertThat用法

    2019独角兽企业重金招聘Python工程师标准>>> 一.JUnit基本介绍 1.简介 JUnit是一个开放源码的.Java语言的单元测试框架,用于测试期望结果的断言(Assert ...

  2. go benchmark 性能测试 单元测试 基准测试 使用方法详解

    简言 基准测试是测量一个程序在固定工作负载下的性能 在Golang中,基准测试函数以Benchmark为前缀并且带有一个 *testing.B 类型的参数 规则 基准测试的代码文件必须以_test.g ...

  3. Android 单元测试 Mockito使用详解

    简介 笔者的Android单元测试相关系列: Android单元测试:Mockito使用详解 Android单元测试:使用本地数据测试Retrofit Android单元测试:测试RxJava的同步及 ...

  4. qt布局嵌套_QDockWidget嵌套布局详解-实现Visual Studio布局

    概述 许多工程软件,如Qt Creator,VS,matlab等,都是使用dock布局窗口,这样用户可以自定义界面,自由组合窗口. Qt的嵌套布局由QDockWidget完成,用Qt Creator拖 ...

  5. python 单元测试 工具_Django单元测试工具test client使用详解

    The test client test client是一个python类,来模拟一个简单的"哑"浏览器,允许你来测试你的view函数.你可以使用test client完成下列事情 ...

  6. Spring Boot 单元测试详解+实战教程

    转载自   Spring Boot 单元测试详解+实战教程 Spring Boot 的测试类库 Spring Boot 提供了许多实用工具和注解来帮助测试应用程序,主要包括以下两个模块. spring ...

  7. python代码覆盖率测试_unittest+coverage单元测试代码覆盖操作实例详解_python

    这篇文章主要为大家详细介绍了unittest+coverage单元测试代码覆盖操作的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 基于上一篇文章,这篇文章是关于使用coverage来实现代码 ...

  8. python单元测试框架Unittest详解

    前言 我们今天来聊聊Python中的单元测试框架unittest,大家都知道在Python中比较热门的测试框架除了pytest就是unittest,我之前有讲过pytest所以今天想讲unittest ...

  9. Android 8.0学习(32)---Android 8.0源码目录结构详解

    Android 8.0源码目录结构详解 android的移植按如下流程:     (1)android linux 内核的普通驱动移植,让内核可以在目标平台上运行起来.     (2)正确挂载文件系统 ...

最新文章

  1. 使数组中奇数位于偶数前面
  2. 使用Cbrgen和Setdest构造无线仿真环境
  3. 架设SharePoint工作组网站(上)
  4. 静态链接库和动态链接库(转)
  5. 3.4 matlab用for语句实现循环结构
  6. ibm招mysql_IBM-ETP实训之MySql基本命令总结
  7. 回客科技 面试的 实现ioc 容器用到的技术,简述BeanFactory的实现原理,大搜车面试的 spring 怎么实现的依赖注入(DI)...
  8. day03_顺丰快递分拣小程序
  9. ir2110s驱动工作原理
  10. java.lang.UnsatisfiedLinkError解决方法
  11. 0-SIM卡的迭代(SIM-USIM-eSIM-vSIM-softSIM)
  12. 美团数据分析岗面试题分享
  13. 新旧笔记本电脑怎么样转移数据?换电脑数据如何迁移
  14. 1999~2019 年,互联网二十载野蛮生长史!
  15. Spring Security OAuth2 实现多人登录互踢下线
  16. Linux常见查看命令
  17. 太阳上挖煤的可行性报告
  18. 计算机软件工作时间,电脑上使用的工作日程表软件用哪个?
  19. 无需其他格式转换软件,就用itunes轻松将m4a转为mp3
  20. 今天看了公司的聘用合同,如有兼职需罚款10万元

热门文章

  1. matlab中的case语句,MATLAB switch语句
  2. html js 无限循环,JavaScript For 循环
  3. DM-DM DBLINK使用配置
  4. 利用adobe audition cc2014中置声道提取制作伴奏
  5. 【ST89C51单片机显示电路设计(Altium Designer 10)----绘制PCB原理图双层板】
  6. 全球生物基尼龙主要生产企业大盘点
  7. Xilinx Vivado和SDK安装
  8. lucene原理及java实现_Lucene字典的实现原理
  9. 软件工程专业需要知道的缩写和专业名词
  10. 一文带你入门 JMeter 性能测试!