【问题标题】:Unit Testing ActiveAndroid Models Using Robolectric使用 Robolectric 对 ActiveAndroid 模型进行单元测试
【发布时间】:2015-04-10 02:03:39
【问题描述】:

我正在为我的一些模型使用 ActiveAndroid,我想开始对我的工作进行单元测试。不幸的是,我遇到了很多错误,即无法使用正确的上下文初始化 ActiveAndroid。

ActiveAndroid 被初始化:

ActiveAndroid.initialize(context)

我尝试通过以下方式初始化上下文:

  1. 有一个扩展应用程序的存根类,并使用它来初始化数据库。

    private class TestApp extends com.activeandroid.app.Application{
       @Override
       public void onCreate() {
         super.onCreate();
         initialiseDB(getDatabaseName());
       }
    
       protected String getDatabaseName() {
         return "sad";
       }
    
       private void initialiseDB(String dbName) {
          ActiveAndroid.initialize(this); 
       }
    }
    

这失败了,因为 .getPackageName() 和 .getApplicationContext() 的类返回 null,这两者都由初始化在内部使用。

我也尝试过使用 ShadowContextWrapper,但我可能用错了。这是我的做法:

    ShadowContextWrapper shadowContextWrapper = new ShadowContextWrapper();
    shadowContextWrapper.setApplicationName("appName");
    shadowContextWrapper.setPackageName("package");
    Context context = shadowContextWrapper.getApplicationContext();

此方法在 ShadowContextWrapper.java:52 处出现 NPE 失败 Robolectric 的哪个部分。线路本身:

    Context applicationContext = this.realContextWrapper.getBaseContext().getApplicationContext();

我正在使用 AS 1.2、robolectric3.0 和 activeandroid 3.1。

这是我正在运行的测试示例。

@RunWith(CustomRobolectricTestRunner.class)
   public class ItemTest {

     public void setUp(){

     }

    @Test
    public void checkJUnitWork() {
       assertThat(true, is(true));
    }

    @Test
    public void testSave(){
       Item item = new Item("name", "units", 5.0, 4.5, 10.0);
       assertThat(item.getName(),is("name"));
    }

   public void tearDown(){

   }
}

我的自定义Runner如下:

public class CustomRobolectricTestRunner extends RobolectricTestRunner {

public CustomRobolectricTestRunner(Class<?> testClass)
        throws InitializationError {
    super(testClass);
    String buildVariant = (BuildConfig.FLAVOR.isEmpty()
            ? "" : BuildConfig.FLAVOR+ "/") + BuildConfig.BUILD_TYPE;
    String intermediatesPath = BuildConfig.class.getResource("")
            .toString().replace("file:", "");
    intermediatesPath = intermediatesPath
            .substring(0, intermediatesPath.indexOf("/classes"));

    System.setProperty("android.package",
            BuildConfig.APPLICATION_ID);
    System.setProperty("android.manifest",
            intermediatesPath + "/manifests/full/"
                    + buildVariant + "/AndroidManifest.xml");
    System.setProperty("android.resources",
            intermediatesPath + "/res/" + buildVariant);
    System.setProperty("android.assets",
            intermediatesPath + "/assets/" + buildVariant);

    ShadowContextWrapper shadowContextWrapper = new ShadowContextWrapper();
    shadowContextWrapper.setApplicationName("appName");
    shadowContextWrapper.setPackageName("package");
    Context context = shadowContextWrapper.getApplicationContext();

    ActiveAndroid.initialize(context);

}

}

【问题讨论】:

  • 因为它是单元测试,所以我会从测试中删除 ActiveRecord
  • 您能否也添加您的测试代码和您用于测试的清单文件?我偷偷怀疑 Robolectric 创建应用程序的方式可能有些奇怪
  • @EugenMartynov 嘿。我想过这样做。但是我的模型上有一些业务逻辑,我想对其进行测试。另外,我想做一些数据库工作,比如安装夹具数据并对其进行测试。因此,我真的需要这个来工作。
  • @abest 我不确定你指的是什么清单。我已经包含了我的测试运行器类,以及一个示例测试。
  • 我应该更清楚地说,我将删除对数据库的依赖,以消除与数据库无关的所有测试。我鼓励你阅读这篇文章medium.com/@artem_zin/…

标签: android unit-testing robolectric android-context activeandroid


【解决方案1】:

所以,您在测试中遇到的问题是 TestApp 没有运行。要使其运行,您需要将测试设置为使用将 TestApp 指定为要运行的应用程序的清单。

在源代码树的/test 目录中的某处设置您的TestApp...例如/src/test/java/some-long-package/TestApp.java:

package com.some.company;

public class TestApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ActiveAndroid.initialize(this);
    }
}

这是重要的部分

在源代码的 /test 树中创建一个 android 清单文件。让此清单文件将 TestApp 指定为应用程序。因此,在 /src/test/resources/TestManifest.xml 之类的路径上创建一个清单,其中包含以下内容:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.test">

        <application android:name="com.some.company.TestApp">

    </application>
</manifest>

我建议去掉 CustomRobolectricTestRunner,因为默认的 Robolectric 3.0 测试运行器将完成您需要完成的大部分工作。如果您需要测试各种构建变体,请使用@RunWith(RobolectricGradleTestRunner.class)

但现在,请按如下方式设置您的测试:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, manifest = "src/test/resources/TestManifest.xml", sdk = Build.VERSION_CODES.LOLLIPOP)
public class MainAppTest {

    @Test
    public void runtimeApplicationShouldBeTestApp() throws Exception {
       String actualName = RuntimeEnvironment.application.getClass().getName();
       String expectedName = TestApp.class.getName();
       assert(actualName).equals(expectedName);
    }
}

@Config(manifest= ...) 位将设置 Robolectric 以使用测试清单和测试应用程序。上面的简单测试验证了测试中使用的应用程序上下文确实是TestApp.class 这将确保 ActiveAndroid 为测试正确初始化。

我也同意 Eugen 的观点,即您可能会尝试在测试中做一些事情。通过您的应用程序测试您的数据库,您正在有效地创建一个集成测试。我建议尽可能多地拆分功能。

测试愉快!

【讨论】:

  • 谢谢。这让我走上了解决问题的正确道路。关于测试,AA 的工作方式,DataModels 的构造函数在没有 db 的情况下将无法工作。因此,即使没有测试 CRUD,初始化也会失败。再次感谢您。
猜你喜欢
  • 2019-07-12
  • 2020-04-13
  • 2016-11-13
  • 2014-05-17
  • 1970-01-01
  • 2016-06-15
  • 2018-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多