Robolectric是如何做到这点的呢?
  Robolectric使用了javassist在运行时动态修改Android.jar中类的byte code,Robolectric会在JVM加载Android.jar包的时候,重写其中类的方法。Roblectroic会让这些方法有返回值(null或是0)而不是抛出异常,或者将这些方法调用转向Shadow Objects来模拟Android SDK的实现。Shadow Objects是Robolectric在运行时插入到Android.jar包相应的类中的,它们会实际处理方法的调用,并记录相应的状态,以备在assert的时候进行查询。如图所示。Robolectric提供了大量的Shadow Objects,覆盖了测试开发过程中绝大多数逻辑功能的需要。

  Robolectric的使用
  基于Robolectric的测试需要使用其特定的test runner(RobolectricTestRunner)来运行,我们可以通过扩展RobolectricTestRunner来创建一个自己的test runner,并在其构造函数中设定需要加载的AndroidManifest.xml和resource目录。如:
  public class MyTestRunner extends RobolectricTestRunner{
  public MyTestRunner(Class<?>testClass)throws InitializationError{
  super(testClass,new RobolectricConfig(new File("my_app/AndroidManifest.xml"),new File("my_app/res")));
  }
  }
  有了自己的test runner,我们可以来写一个简单的Robolectric测试了
  1@RunWith(MyTestRunner.class)
  public class SignInScreenTest{
  @Test
  public void should_start_intent_when_click_registration_button(){
  2 Activity activity=new Activity();
  SignInScreen signInScreen=new SignInSceen(activity);
  3 TextView textView=(TextView)signInScreen.findViewById(R.id.sign_in_registration);
  textView.performClick();
  4 ShadowActivity shadowActivity=Robolectric.shadowOf(activity);
  Intent nextStartedActivity=shadowActivity.getNextStartedActivity();
  ShadowIntent shadowIntent=Robolectric.shadowOf(nextStartedActivity);
  assertThat((Class<WebPageActivity>)shadowIntent.getIntentClass(),equalTo(WebPageActivity.class));
  }
  }
  在这段测试代码中:
  (1)声明了测试运行的test runner;像普通的单元测试,它也分为了set up,method invoke,以及assert三个阶段。
  在(2)中,测试初始化了一个Activity用于提供Context,并使用这个Activity对象生成了一个SignInScreen实例;
  第二个阶段,也是(3)中,代码在生成的登录界面中找到注册按钮,并进行点击。为有意思的第三个阶段需要验证注册按钮的点击触发了我们期望的事件,即使用Implicit Intent来打开WebPageActivity。
  为了进行这个验证,(4)中首先通过Robolectric的静态方法shadowOf来获取activity对象相应的Shadow Object,而通过这个Shadow Object,代码获得了activity对象的所开启的Intent对象。后通过Intent对象的Shadow Object,我们可以获得其intent class并进行验证。
  通过这个测试我们可以看到,有了Robolectric的帮助,我们可以轻松的生成Activity实例,加载xml布局文件,进行组件上的方法调用。通过shadow对象,我们则可以获取Android相关类的对象状态信息,来对测试的结果进行验证。实际上除了Intent,我们还可以对通过使用Robolectric对代码中的Dialog,HTTP请求,数据库操作等各个方面进行测试。
  Robolectric并没有为Android SDK中的所有类都定义shadow对象,你可以通过调用Robolectric.getDefaultShadowClasses()方法来查看你所需要的类是否已经被注册到了需要被shadow的类列表中。如果没有你可能需要对其进行定制和扩展。关于如何添加Shadow Objects而增加Robolectric的功能,在Robolectric的网站文档中有详细的描述。
  由于Robolectric的测试是可以脱离Android的SDK运行于JVM上,我们可以像运行普通的jUnit测试一样在IDE中或者在终端使用构建脚本运行我们的测试。
  由于Robolectric的更新并不是很频繁,我们在平时也遇到了一些需要定制的情况,如支持Android4.0,使用sonar进行项目质量分析等等。所以我们在github上fork了Robolectric的工程,并以git submodule的方式将其加入到我们的工程管理中来,这样,我们可以根据自己的需要来对Robolectric进行修改和扩展。由于我们对Robolectric的修改频率非常的低,在每一次修改后,可以将其编译打包成一个jar文件,将这个jar文件加入到我们的工程管理中,让我们的测试代码仍然依赖于这个jar文件,这样可以免去在运行测试中不必要的对Robolectric的重复编译,加快测试代码的运行速度。
  我们在当前的项目中也进行了一定的关于验收测试方面的尝试,由于测试脚本是开发人员与BA以及QA进行沟通的一种重要途径,也是开发人员和QA进行人工测试的基准,因此我们仍然选用了cucumber作为我们编写脚本的工具,再使用cuke4duke和jRuby对其进行解析和执行。但目前这种测试方式似乎并不成熟,我们在这种尝试和实践的过程中遇到了种种的问题,主要在于测试编写和维护上的困难,这也导致了我们验收测试的覆盖率并不高。我们也会在这一方向上进行更多的尝试,如果大家有更好的关于验收测试自动化方面的实践,也希望能够得到你们的帮助和指正。