Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1570892
  • 博文数量: 113
  • 博客积分: 3526
  • 博客等级: 中校
  • 技术积分: 1815
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-08 09:46
个人简介

记录总结自己的工作

文章分类

全部博文(113)

文章存档

2015年(19)

2014年(10)

2013年(6)

2012年(16)

2011年(24)

2010年(21)

2009年(17)

分类: 嵌入式

2012-05-21 13:42:43

        想要更好地使用Android的TDD,应用MOCK是必不可少的。那么MOCK又是什么呢?说白了MOCK就是一系列的模拟类,在TDD中使用这些MOCK的类来代替真实的类。那为什么要用MOCK呢?
     
       为了更好的说明用MOCK的理由,请看下图:

                       

        在这里模块A依赖于模块B,C,D,而我们要测的就是模块A。但是它有依赖,而且这些依赖说不定现在根本都还没实现或者很复杂,在这种情况下,如果要测模块A,MOCK就十分有必要了。如下图所示,我们将模块B,C,D都使用MOCK来代替,这样就可以消除A的所有依赖,让我们专注于A的测试了。

                       

        说起来容易,但是怎么用MOCK来代替所依赖的部分呢?又怎么来实现这些MOCK呢?

        打开android.jar的android.test.mock目录,可以看到里面有很多Mockxxx类,这些都是ANDROID提供给我们的可以直接继承来实现的MOCK类,比如说你想创建一个MOCK CURSOR,就可以直接继承MockCursor,然后再加上一些自己的实现就可以了,这个我在下文会做详细的说明。当然可以MOCK的不仅限于这几个类,基本所有的类都可以MOCK,只要继承这个类并且重写你会用到的方法就可以了。当然如果类中有private的方法想要mock就比较麻烦了。

       假如说一个应用中有一个ListView,ListView的内容来自于查询content provider得到的cursor。在对这个list进行TDD的时候要怎么来MOCK这个cursor呢?现来看一下android程序是如何得到cursor的呢?没错,用的是getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder),这个方法是Context里面的,而Resolver又会找它注册的那个content provider来查询得到cursor,所以说要得到一个mock cursor,就要按照下面的顺序来:

      MockContext->MockContentResolver->MockContentProvider->MockCursor。

      所以说第一步要做的工作就是将系统的Content替换为我们mock的MockContext。这一步需要在程序的代码中添加一些额外的实现。一种实现方法是在intent中将mockContext的类名传进去,然后在代码实例化mockContext。
     

点击(此处)折叠或打开

  1. public void onCreate(Bundle savedInstanceState) {
  2.         super.onCreate(savedInstanceState);
  3.         setContentView(R.layout.main);
  4.         String contextWrapperClassName = null;
  5.         Bundle extras = getIntent().getExtras();
  6.         if (extras != null) {
  7.             contextWrapperClassName = extras
  8.                     .getString(EXTRA_CONTEXT_WRAPPER);
  9.          
  10.         }

  11.         if(contextWrapperClassName!=null){
  12.             mContextWrapper = Class.forName(contextWrapperClassName).newInstance();
  13.         }else{
  14.             mContextWrapper = new ContextWrapper(this.getApplicationContext());
  15.         }
  16.        
  17.         
  18.     }
          ok,这样在Test工程中启动Activity的时候将MockContext的类名加入到Intent中,程序运行起来的时候mContextWrapper将会被替换成我们的MockContext,在程序中需要使用getContentResolver()的时候都使用mContextWrapper.getContentResolver(),这样ContentResolver就会被替换成我们的MockContextResolver。当然要实现这个在MockContext中还要将MockContextProview给注册上。

点击(此处)折叠或打开

  1. public class MyMockContext extends ContextWrapper {
  2.    
  3.     private MyMockContentResolver mContentResolver;

  4.     public MyMockContext(Context targetContext) {
  5.         super(targetContext);
  6.         mContentResolver = new MyMockContentResolver();
  7.         mContentResolver.addProvider(MYAUTHORITY, new MyMockContentProvider());
  8.     
  9.     }
  10.     public ContentResolver getContentResolver() {

  11.         return mContentResolver;
  12.     }
  13. }
       在这里面需要注意是MYAUTHORITY,这里应该写的是真实ContentProvider的上authority。这样程序在找Contentprovider的时候才会找到我们的MyMockContentProvider。当然MockContext的作用不仅仅是这样的,可以说MockContext是Mock的核心,通过MockContext可以实现很多Mock的东西。

       对于MyMockContextResolver,不需要什么实现,只要创建这样一个类使其集成MockContextResolver就可以了。但是MyMockContextProvider就需要加上实现的代码了:

点击(此处)折叠或打开

  1. public class MyMockContentProvider extends MockContentProvider {
  2.     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
  3.         MyMockCursor cursor = new MyMockCursor(uri.toString());
  4.         return cursor;
  5.     }
  6. }
      这样终于可以指向MockCursor了,当然在这里可以对query的uri, projection等做一下处理,如果有多个查询的话就可以返回不同的MockCursor了。

      对于MockCursor,要实现的地方就很多了,具体要实现哪些方法也取决于程序中调用了哪些方法。一个简单的MockCursor可以这样;

点击(此处)折叠或打开

  1. public class MyMockCursor extends MockCursor {
  2.     
  3.     public MyMockCursor(String uri) {
  4.        
  5.     }
  6. }
        这个MockCursor内部没有任何数据,也没有实现任何方法,直接使用的话肯定会出错。至于内部的数据,我们可以使用一个ArrayList来实现,这样getCount()方法就可以返回ArrayList的长度,getString方法就可以返回ArrayList中的第n条数据等等。对于列名和列号可以分别存储在两个map中,这样getColumnIndex和getColumnName可以直接查询这两个map就可以了。


        一定有人会问,为什么我要继承MockCursor这个类啊,我直接实现Cursor不行吗?其实也不是不可以,只是Cursor是一个接口,内部有N多的方法,如果直接继承Cursor的话就必须要实现所有的方法,而其中有一些你根本就用不着,但是如果继承MockCursor的话你只需要实现需要用的就可以了,一个词:方便。

阅读(9560) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

云少嘎嘎嘎2012-05-22 10:26:08

十七岁的回忆: 恩,受教了~刚刚入门的我要多多学习啊~~.....
呵呵,慢慢来啊。刚开始学都这样。

十七岁的回忆2012-05-21 21:42:05

恩,受教了~刚刚入门的我要多多学习啊~~