使用 SugarORM 和 Robolectric 进行测试
Testing with SugarORM and Robolectric
我正在尝试为我的 android 项目设置测试环境。基本 Robolectric 设置已完成。我用这个不错tutorial。如果我在 Manifest.xml 中注释掉 SugarORM,所有测试都可以正常工作。但是如果我想将它与 SugarORM 一起使用,我总是会收到此错误:
java.lang.NullPointerException at
dalvik.system.DexFile$DFEnum.hasMoreElements(DexFile.java:239) at
com.orm.SugarDb.getDomainClasses(SugarDb.java:37) at
com.orm.SugarDb.createDatabase(SugarDb.java:104) at
com.orm.SugarDb.onCreate(SugarDb.java:100) at
android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252)
at
android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at com.orm.Database.getDB(Database.java:18) at
com.orm.SugarApp.onTerminate(SugarApp.java:16) at
org.robolectric.internal.ParallelUniverse.tearDownApplication(ParallelUniverse.java:133)
at
org.robolectric.RobolectricTestRunner.evaluate(RobolectricTestRunner.java:246)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290) at
org.junit.runners.ParentRunner.schedule(ParentRunner.java:71) at
org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at
org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58) at
org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268) at
org.robolectric.RobolectricTestRunner.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at
org.junit.runner.JUnitCore.run(JUnitCore.java:137) at
com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
大家都有同样的问题吗?
编辑
可能是,因为Robolectric测试后关机,sugarorm还没有完成。我的测试类
@RunWith(RobolectricTestRunner.class)
@Config(manifest="./src/main/AndroidManifest.xml",emulateSdk=18)
public class MyActivityTest{
private ActivityController<OnLogActivity> controller = Robolectric.buildActivity(OnLogActivity.class).create().setup();
@Test
public void clickingLogin_shouldStartLoginActivity() {
OnLogActivity activity = controller.get();
assertTrue(activity.findViewById(R.id.login) instanceof Button);
}
}
编辑 2.0:
那 Robolectric 可以找到 android:name=com.orm.SugarApp
您必须使用与 com.orm 相同的包创建一个测试文件夹并添加一个名为 TestSugarApp 的测试 class。之后你就可以测试所有的东西了。
package com.orm;
...
@RunWith(RobolectricTestRunner.class)
@Config(manifest="./src/main/AndroidManifest.xml",emulateSdk=18)
public class TestSugarApp extends Application
implements TestLifecycleApplication {
private ActivityController<OnLogActivity> controller;
@Test
public void startEverTestSugarAppAsFirst() {
}
@Override
public void beforeTest(Method method) {
Log.v("test", "beforeTest");
}
@Override
public void prepareTest(Object test) {
Log.v("test", "prepareTest");
}
@Override
public void afterTest(Method method) {
Log.v("test", "afterTest");
}
}
好的,下一步试试。添加下一个 class 给你测试代码:
public class TestSugarApp
extends SugarApp
{
@Override
public void onCreate() {}
@Override
public void onTerminate() {}
}
class 命名的 Test 将被 Robolectric 加载和使用,您可以覆盖一些与测试无关的东西。我试图阻止在 onCreate
和 onTerminate
(https://github.com/satyan/sugar/blob/master/library/src/com/orm/SugarApp.java).
中执行来自 SugarApp
的代码
@Eugen Martynov 的回答非常有效(非常感谢!)。
但是,当您自己扩展 android.app.Application(它又扩展了 SugarApp)时,您也会排除自己的代码。
我已经使用以下代码解决了这个问题:
测试代码中的Applicationclass(与Eugen的回答相反):
public class TestApp extends App {
/** Prevents onCreate() and onTerminate() to call their super (in which {@link com.orm.SugarApp} is initialized) */
@Override
protected boolean callSuper() {
return false;
}
}
我自己的应用程序class(扩展 SugarApp):
public class App extends com.orm.SugarApp {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
if (callSuper()) {
super.onCreate();
}
// My own code is always executed, also during unittests
context = getApplicationContext();
}
@Override
public void onTerminate() {
if (callSuper()) {
super.onTerminate();
}
}
protected boolean callSuper() {
return true; // Super is executed by default
}
}
我同意它看起来不是很漂亮(有什么建议可以让它变得更好吗?),但它非常适合我!
ps。我在清单中这样声明我的应用程序 class:
<application
android:name=".App"
我正在尝试为我的 android 项目设置测试环境。基本 Robolectric 设置已完成。我用这个不错tutorial。如果我在 Manifest.xml 中注释掉 SugarORM,所有测试都可以正常工作。但是如果我想将它与 SugarORM 一起使用,我总是会收到此错误:
java.lang.NullPointerException at dalvik.system.DexFile$DFEnum.hasMoreElements(DexFile.java:239) at com.orm.SugarDb.getDomainClasses(SugarDb.java:37) at com.orm.SugarDb.createDatabase(SugarDb.java:104) at com.orm.SugarDb.onCreate(SugarDb.java:100) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:252) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) at com.orm.Database.getDB(Database.java:18) at com.orm.SugarApp.onTerminate(SugarApp.java:16) at org.robolectric.internal.ParallelUniverse.tearDownApplication(ParallelUniverse.java:133) at org.robolectric.RobolectricTestRunner.evaluate(RobolectricTestRunner.java:246) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner.run(ParentRunner.java:290) at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58) at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268) at org.robolectric.RobolectricTestRunner.evaluate(RobolectricTestRunner.java:158) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
大家都有同样的问题吗?
编辑
可能是,因为Robolectric测试后关机,sugarorm还没有完成。我的测试类
@RunWith(RobolectricTestRunner.class)
@Config(manifest="./src/main/AndroidManifest.xml",emulateSdk=18)
public class MyActivityTest{
private ActivityController<OnLogActivity> controller = Robolectric.buildActivity(OnLogActivity.class).create().setup();
@Test
public void clickingLogin_shouldStartLoginActivity() {
OnLogActivity activity = controller.get();
assertTrue(activity.findViewById(R.id.login) instanceof Button);
}
}
编辑 2.0:
那 Robolectric 可以找到 android:name=com.orm.SugarApp
您必须使用与 com.orm 相同的包创建一个测试文件夹并添加一个名为 TestSugarApp 的测试 class。之后你就可以测试所有的东西了。
package com.orm;
...
@RunWith(RobolectricTestRunner.class)
@Config(manifest="./src/main/AndroidManifest.xml",emulateSdk=18)
public class TestSugarApp extends Application
implements TestLifecycleApplication {
private ActivityController<OnLogActivity> controller;
@Test
public void startEverTestSugarAppAsFirst() {
}
@Override
public void beforeTest(Method method) {
Log.v("test", "beforeTest");
}
@Override
public void prepareTest(Object test) {
Log.v("test", "prepareTest");
}
@Override
public void afterTest(Method method) {
Log.v("test", "afterTest");
}
}
好的,下一步试试。添加下一个 class 给你测试代码:
public class TestSugarApp
extends SugarApp
{
@Override
public void onCreate() {}
@Override
public void onTerminate() {}
}
class 命名的 Test 将被 Robolectric 加载和使用,您可以覆盖一些与测试无关的东西。我试图阻止在 onCreate
和 onTerminate
(https://github.com/satyan/sugar/blob/master/library/src/com/orm/SugarApp.java).
SugarApp
的代码
@Eugen Martynov 的回答非常有效(非常感谢!)。 但是,当您自己扩展 android.app.Application(它又扩展了 SugarApp)时,您也会排除自己的代码。
我已经使用以下代码解决了这个问题:
测试代码中的Applicationclass(与Eugen的回答相反):
public class TestApp extends App {
/** Prevents onCreate() and onTerminate() to call their super (in which {@link com.orm.SugarApp} is initialized) */
@Override
protected boolean callSuper() {
return false;
}
}
我自己的应用程序class(扩展 SugarApp):
public class App extends com.orm.SugarApp {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
if (callSuper()) {
super.onCreate();
}
// My own code is always executed, also during unittests
context = getApplicationContext();
}
@Override
public void onTerminate() {
if (callSuper()) {
super.onTerminate();
}
}
protected boolean callSuper() {
return true; // Super is executed by default
}
}
我同意它看起来不是很漂亮(有什么建议可以让它变得更好吗?),但它非常适合我!
ps。我在清单中这样声明我的应用程序 class:
<application
android:name=".App"