Android单元测试框架Robolectric3.0的使用

单元测试是什么

单元测试 是针对 程序的最小单元 来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。一个单元可能是单个程序、类、对象、方法等。

单元测试意义

  • 减少bug
  • 快速定位bug
  • 提高代码质量
  • 减少调试时间

Robolectric3.0环境搭建

在build.gradle中配置如下依赖关系:

testCompile "org.robolectric:robolectric:3.0"

通过注解配置TestRunner

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class SampleActivityTest {

}

说明:上面配置的是RobolectricTestRunner,而不是RobolectricGradleTestRunner,在Robolectric之前的版本是有这个RobolectricGradleTestRunner,但在最新的版本上却没有了,也不知道是为什么。但是有一点,使用最新版本后,倒是没有出现找不到资源文件res的警告。最新的Robolectric最高可支持Android API 23。

Android Studio的配置

working directory 设置为$MODULE_DIR$
如果在测试过程遇见如下问题,解决的方式就是设置working directory的值:

java.io.FileNotFoundException: build\intermediates\bundles\debug\AndroidManifest.xml (系统找不到指定的路径。)

设置方法如下图所示:

Edit Configurations
Working directory的配置

编写Activity测试用例代码

public class ComplaintActivityTest extends BaseRobolectricTestCase {

@Test
@PrepareForTest({AppUtil.class, OAuthManager.class, NetUtil.class})
public void jumpCompensate() throws Exception {
    PowerMockito.mockStatic(AppUtil.class);
    PowerMockito.when(AppUtil.getVersionName()).thenReturn("1.4.0");

    PowerMockito.mockStatic(OAuthManager.class);
    OAuthManager mockOAuth = PowerMockito.mock(OAuthManager.class);
    PowerMockito.when(OAuthManager.getInstance()).thenReturn(mockOAuth);
    PowerMockito.when(mockOAuth.getSargerasToken()).thenReturn("c97faa92-34ea-4248-a19e-9a9fb848b29b");

    AppApplication.mInstance = getApplication();

    PowerMockito.mockStatic(NetUtil.class);
    PowerMockito.when(NetUtil.isNetworkConnected(AppApplication.getInstance())).thenReturn(true);

    PreferenceUtil.init();
    PersistentPreferenceUtil.init();

    ComplaintActivity complaintActivity = Robolectric.buildActivity(ComplaintActivity.class).create().get();
    assertNotNull(complaintActivity);
    complaintActivity.jumpCompensate();
    Intent expectedIntent = new Intent(complaintActivity, HelpActivity.class);
    ShadowActivity shadowActivity = Shadows.shadowOf(complaintActivity);
    Intent actualIntent = shadowActivity.getNextStartedActivity();
    Assert.assertEquals(expectedIntent.getComponent().getClassName(), actualIntent.getComponent().getClassName());
}

}

上面前一部分代码主要设置ComplaintActivity运行所依赖的属性,这也是在单元测试最为繁琐的地方,因为不是运行在真实的Android环境中。具体分析如下:

  1. 通过注解@PrepareForTest({AppUtil.class, OAuthManager.class, NetUtil.class})定义PowerMockito要mock的类;

  2. 在Robolectric中读取不到apk的版本号,通过PowerMockito.when(AppUtil.getVersionName()).thenReturn("1.4.0")mock指定AppUtil.getVersionName()的返回值"1.4.0",即版本号;

  3. 通过AppApplication.mInstance = getApplication();使用Robolectric运行环境中的application对AppApplication.mInstance进行依赖注入,因为在很多类中都会用到AppApplication.mInstance进行初始化,例如SharedPreference、SQlite、单例类等;

  4. ComplaintActivity complaintActivity = Robolectric.buildActivity(ComplaintActivity.class).create().get();使用Robolectric创建ComplaintActivity对象,其中create()方法就是对应于调用Activity生命周期的onCreate()方法,此外Robolectric支持链式调用如:Robolectric.buildActivity(ComplaintActivity.class).create().resume().get();

  5. assertNotNull(complaintActivity);验证complaintActivity是否跑起来;

  6. 最后一部分代码就是调用jumpCompensate方法进行跳转,验证跳转的Intent是否符合预期;

至于其他的一些如Fragment、Dialog、Toast等验证,可以参考这篇博客,这里就不展开。至于其他的一些如Fragment、Dialog、Toast等验证,可以参考这篇博客,这里就不展开。

Robolectric常见的坑

1. Application空指针问题

这是因为SharedPreferences和单例等类初始化时需要依赖Application对象,我们常见的用法是使用Application.getApplication()方法来获取,在Robolectric中则是需要使用RuntimeEnvironment.application来进行替换,上面就是通过依赖的方式进行替换。

2. AppCompatActivity错误

假如你在Robolectric的@Config注解中配置了manifest = Config.NONE,那就完蛋了,因为在网上根本找不解决的方法,你遇到如下异常不能使用support V7包的类:

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

 at android.support.v7.app.AppCompatDelegateImplV7.createSubDecor(AppCompatDelegateImplV7.java:343)
 at android.support.v7.app.AppCompatDelegateImplV7.ensureSubDecor(AppCompatDelegateImplV7.java:312)
 at android.support.v7.app.AppCompatDelegateImplV7.initWindowDecorActionBar(AppCompatDelegateImplV7.java:172)
 at android.support.v7.app.AppCompatDelegateImplBase.getSupportActionBar(AppCompatDelegateImplBase.java:88)
 at android.support.v7.app.AppCompatActivity.getSupportActionBar(AppCompatActivity.java:110)
 at me.ele.shopcenter.components.BaseActivity.initActionBar(BaseActivity.java:104)
 at me.ele.shopcenter.components.BaseActivity.onCreate(BaseActivity.java:52)
 at me.ele.shopcenter.ui.order.ComplaintActivity.onCreate(ComplaintActivity.java:93)
 at android.app.Activity.performCreate(Activity.java:6251)
 at org.robolectric.util.ReflectionHelpers.callInstanceMethod(ReflectionHelpers.java:231)

解决的方式就是去掉manifest = Config.NONE配置,这是坑爹的,我就遇到这个错误,花了好长一段时间才发现是这个配置导致的。

3. 找不到android.net.http.AndroidHttpClient的类文件

在Android API23开始,google就移除了HttpClient相关的类,有两种方法解决上述问题。
在build.gradle添加应用useLibrary ‘org.apache.http.legacy’

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

推荐阅读更多精彩内容