单元测试Robolectric的使用详解
来源:互联网 发布:万方数据库下载论文 编辑:程序博客网 时间:2024/06/06 03:13
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;
@Overridepublic 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() { @Override public 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() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }).create(); alertDialog.show();}@Overrideprotected void onStart() { super.onStart(); name="start";}@Overrideprotected void onResume() { super.onResume(); name="jump";}@Overrideprotected void onPause() { super.onPause(); name="pause";}@Overrideprotected void onStop() { super.onStop(); name="stop";}@Overrideprotected void onDestroy() { super.onDestroy(); name="destroy"; unregisterReceiver(receiver);}@Overridepublic 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;
@Beforepublic 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);}@Afterpublic void tearDown() { mActvity = null; mbtn = null; mTv = null;}//测试启动初始化是否成功@Testpublic 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());}//测试点击事件是否相应@Testpublic void testClickButton() { mbtn.performClick(); Assert.assertEquals("textView text is error", "我被修改了", mTv.getText().toString());}//测试Actvity生命周期的调用@Testpublic 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 生命周期@Testpublic void testFragmentLifecyle() { FragmentController<QxFramentApp> controller = Robolectric.buildFragment(QxFramentApp.class); QxFramentApp framentApp = controller.get(); controller.create();////这样一步就走到了onCreatView不仅仅走了onCraete Assert.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的跳转@Testpublic 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添加初始化是否成功@Testpublic void testFragment() { QxFragment fragment = new QxFragment(); //把Fragment添加到Activity中,并且启动Fragment,触发他的生命周期函数OnCreateView SupportFragmentTestUtil.startFragment(fragment); Bundle bundle = fragment.getArguments(); Assert.assertThat("fragment not start success", fragment.getView(), is(notNullValue()));}//测试模拟延迟任务的执行@Testpublic void testDelayTask() { Assert.assertFalse(mActvity.mCount == 6);//此时没有走线程呢所以不会是6 mbtn.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(); */@Testpublic 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()));}@Testpublic void testDialog() { mbtn.performClick(); Dialog dialog = ShadowDialog.getLatestDialog(); Assert.assertNotNull("dialog not creat", dialog); dialog.dismiss(); Assert.assertTrue(!dialog.isShowing());}@Testpublic void testAlertDialog() { mbtn.performClick(); AlertDialog alertDialog = ShadowAlertDialog.getLatestAlertDialog(); Assert.assertNotNull("dialog not creat", alertDialog); alertDialog.dismiss(); Assert.assertTrue(!alertDialog.isShowing());}@Testpublic 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)@Testpublic 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的使用详解
- android robolectric 单元测试的简单使用
- Android单元测试(四):Robolectric框架的使用
- 使用 robolectric 做单元测试
- 使用Robolectric进行Android单元测试
- 使用Robolectric做Android单元测试
- 使用Robolectric对Android应用进行单元测试
- Android中Robolectric的使用
- 初试Robolectric进行单元测试
- Android Studio使用Robolectric对android程序实现单元测试
- Android-使用Mockito、Robolectric和RxJava及Retrofit进行单元测试
- Robolectric使用
- Android学习第九篇;robolectric的使用
- Android stuido 集成 Robolectric 单元测试
- 单元测试--Robolectric及代码覆盖率
- Robolectric使用教程
- 单元测试从Mockito到PowerMock再到Robolectric的详细解析
- Android中单元测试 Robolectric + MVP模式
- CNN卷积神经网络推导和实现
- 使用 maven 部署项目到私服 nexus 出现 Deployment failed 问题的解决方法
- POJ3045 Cow Acrobats
- [Linux]:ssh远程访问内网
- windows下securecrt 连接不上Ubuntu16.10
- 单元测试Robolectric的使用详解
- [置顶] Spring 学习 (二)__BeanWrapper及其实现
- linux下运行程序后出现段错误的原因和解决案例
- jQuery中ajax中sendData自动生成JSON格式的文本
- Homework 2
- 移动前端—图片压缩上传实践
- ROI_PAC处理TSX SLC数据流程
- 表空间组成+创建表空间+修改表空间大小+查看数据文件名称与大小
- WebCollector初学教程