Android robolectric 入门

我的更多 Android 博文

robolectric的最大特别是运行测试程序时不需要设备或者模拟器,在电脑中进行就可以了,自然测试程序的运行效率可以大大提升。

环境搭建

gradle

gradle 中加入

  dependencies {
    ...
    testCompile 'junit:junit:4.10'
    testCompile 'org.robolectric:robolectric:3.0'
  }

==注意==

  • 若写成testCompile,则测试代码放在 test 文件夹中
  • 若写成androidTest,则测试代码放在 androidTest 文件夹中

Android studio 配置

切换成 Unit Tests

在Build Variants窗口内的Test Artifact中选择了"Unit Tests"


Linux 和 Mac 用户需要进行这一步设置、

Run -> Edit Configurations -> Defaults -> Junit

注意Robolectric目前不支持android 5.1 API level 22,编译时 sdk = 21 或者以下。

在project视图中,test 文件夹下,有个绿的的java 文件夹,绿色文件夹表示是单元测试工程。这些代码能够检测目标代码的正确性,打包时单元测试的代码不会被编译进入APK中

Activity

Activity的创建

Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().get();

会创建一个MyAwesomeActivity实例,然后调用它的onCreat()方法

如果只想验证其onResume()方法,可以如下

ActivityController controller = Robolectric.buildActivity(MyAwesomeActivity.class).create().start();
Activity activity = controller.get();
// assert that something hasn't happened
activityController.resume();
// assert it happened!

如果想测试整个activity 的生命周期可以

Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().start().resume().visible().get();

等你需要在activity中,对view进行一些操作时候,比如Robolectric.clickOn(),这时候你需要在create()后加入visible()

举例

我在工程中建立了三个Activity分别为 MainActivity,AnotherActivity,ThridActivity
在MainActivity加入按键跳转到AnotherActivity中,

   public void startAnotherActivity(View view) {
        startActivity(new Intent(MainActivity.this, AnotherActivity.class));
    }

增加单元测试用例判断跳转的是否正确

    @Test
    public void clickingLogin_shouldStartLoginActivity() {
        Button button = (Button) activity.findViewById(R.id.button);
        assertTrue(button.isEnabled());
        button.performClick();

        Intent expectedIntent = new Intent(activity, ThridActivity.class);
        assertEquals(expectedIntent,shadowOf(activity).getNextStartedActivity() );

    }

1.通过 activity.findViewById 找到button
2.判断button是否可以点击
3.点击button
4.构造一个intent(从MainActivity跳转到ThridActivity)
5.判断实际跳转的和构造的是否一致

结构不一致报错

java.lang.AssertionError: 
Expected :Intent{componentName=ComponentInfo{io.github.xuyushi.robolectric/io.github.xuyushi.robolectric.ThridActivity}, extras=Bundle[{}]}
Actual   :Intent{componentName=ComponentInfo{io.github.xuyushi.robolectric/io.github.xuyushi.robolectric.AnotherActivity}, extras=Bundle[{}]}
 <Click to see difference>
    at org.junit.Assert.fail(Assert.java:91)
    at org.junit.Assert.failNotEquals(Assert.java:645)
    at org.junit.Assert.assertEquals(Assert.java:126)
    at org.junit.Assert.assertEquals(Assert.java:145)

可以很清楚的定位到错误

Dialog验证

@Test
public void testDialog(){
     Dialog dialog = ShadowDialog.getLatestDialog();
    assertNotNull(dialog);
 }

Toast验证

@Test
public void testToast(){
    assertEquals(toastContent, ShadowToast.getTextOfLatestToast());
 }

Fragment的测试

如果使用support的Fragment,需添加以下依赖
testCompile "org.robolectric:shadows-support-v4:3.0"

shadow-support包提供了将Fragment主动添加到Activity中的方法:SupportFragmentTestUtil.startFragment(),简易的测试代码如下

@Test
public void testFragment(){
 SampleFragment sampleFragment = new SampleFragment();
 //此api可以主动添加Fragment到Activity中,因此会触发Fragment的onCreateView()
 SupportFragmentTestUtil.startFragment(sampleFragment);
 assertNotNull(sampleFragment.getView());
}

访问资源文件@Test

public void testResources() {
     Application application = RuntimeEnvironment.application;
     String appName = application.getString(R.string.app_name);
     String activityTitle = application.getString(R.string.title_activity_simple);
     assertEquals("test", appName);
     assertEquals("testActivity",activityTitle);
 }

Shodaw

Robolectric的本质是在Java运行环境下,采用Shadow的方式对Android中的组件进行模拟测试,从而实现Android单元测试。对于一些Robolectirc暂不支持的组件,可以采用自定义Shadow的方式扩展Robolectric的功能。

定义原始类

public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

定义 shardow 类

@Implements(User.class)
public class ShadowUser {


    @Implementation
    public String getUsername() {
        return "from ShadowUser";
    }
}

需要让系统知道 ShardowUser 的存在

定义自己的 RobolectricGradleTestRunner 类,继承自RobolectricGradleTestRunner

public class UserShadowTestRunner extends RobolectricGradleTestRunner {
    public UserShadowTestRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    public InstrumentationConfiguration createClassLoaderConfig() {

        InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
        /**
         * 添加要进行Shadow的对象
         */
        builder.addInstrumentedClass(User.class.getName());
        return builder.build();
    }
}

test 测试中使用

Runwith 使用上面自定义的类
同时指明使用 ShadowUser.class,多个类可以加{},中间使用,分开

@RunWith(UserShadowTestRunner.class)
@Config(constants = BuildConfig.class, shadows = ShadowUser.class)
public class LoginActivityTest {

    @Test
    public void testCase() {

        User user = new User("username", "password");
        assertEquals("from ShadowUser", user.getUsername());

    }
}

网络请求 mock

gradle 中加入

    testCompile 'org.robolectric:shadows-httpclient:3.0'
    testCompile 'org.robolectric:robolectric-shadows:3.0'

更多robolectric 包见
http://mvnrepository.com/artifact/org.robolectric

未搞定的

  1. 项目网络请求使用的是 volley ,网络请求并没有找到合适办法模拟
  2. 使用了第三方框架 butterknief,并不能模拟

以上有知道如何解决的 请告诉我一声~ xmy166@gmil.com

参考

http://hkliya.gitbooks.io/unit-test-android-with-robolectric/content/2-api-explained.html
http://www.jianshu.com/p/9d988a2f8ff7
http://robolectric.org
http://chriszou.com/android-unit-testing-with-robolectric

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容