Android 单元测试领域&x2B;匕首2,带机械分子&;莫基托
所以我正在做这个小项目,它使用Dagger2进行依赖注入,并将Realm作为数据库 我正在使用Robolectric和Mockito(使用Powermock)对其进行单元测试。从以前的研究(和很多痛苦)中,我意识到测试领域相当困难,但在过去已经完成了 现在,我的项目的设置和结构与上面链接的项目非常相似 当我运行单元测试时,所有测试都通过了,只有一个测试给了我一个非常神秘的消息,如下所示:Android 单元测试领域&x2B;匕首2,带机械分子&;莫基托,android,unit-testing,realm,robolectric,dagger-2,Android,Unit Testing,Realm,Robolectric,Dagger 2,所以我正在做这个小项目,它使用Dagger2进行依赖注入,并将Realm作为数据库 我正在使用Robolectric和Mockito(使用Powermock)对其进行单元测试。从以前的研究(和很多痛苦)中,我意识到测试领域相当困难,但在过去已经完成了 现在,我的项目的设置和结构与上面链接的项目非常相似 当我运行单元测试时,所有测试都通过了,只有一个测试给了我一个非常神秘的消息,如下所示: java.lang.NullPointerException at org.robolectric.inte
java.lang.NullPointerException
at org.robolectric.internal.ShadowExtractor.extract(ShadowExtractor.java:5)
at org.robolectric.Shadows.shadowOf(Shadows.java:1190)
at org.robolectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:37)
at org.robolectric.util.ComponentController.<init>(ComponentController.java:31)
at org.robolectric.util.ComponentController.<init>(ComponentController.java:23)
at org.robolectric.util.ActivityController.<init>(ActivityController.java:40)
at org.robolectric.util.ActivityController.of(ActivityController.java:32)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:82)
at org.robolectric.Robolectric.buildActivity(Robolectric.java:78)
at org.robolectric.Robolectric.setupActivity(Robolectric.java:86)
at uk.co.placona.tradesafe.view.EditActivityTest.ActivityShouldNotBeNull(EditActivityTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl
该活动存在,并在启动时将TradeRepository注入其中
有问题的活动可以与代码的其余部分一起找到。我已经试着调试了大约3天,但没有成功。我创建的其他每一个单元测试都可以很好地工作,除了活动使用的任何单元测试,这让我觉得我可能遗漏了一些非常明显的东西
我很乐意在这里澄清任何问题。非常感谢 静态是邪恶的,powermock是邪恶的:) 我认为你应该离开你的班级。您不需要它,因为在应用程序的生命周期中只有一个CustomApplication对象 您应该按如下方式修改代码: 在CustomApplication.java中,创建应用程序组件,在字段变量中设置,然后注入
private ApplicationComponent applicationComponent;
public void setup(){
getOrCreateApplicationComponent().inject(this);
databaseRealm.setup();
stethoDebug.setup(this);
}
public ApplicationComponent getOrCreateApplicationComponent() {
if (applicationComponent == null) {
applicationComponent = DaggerApplicationComponent.builder()
.applicationContextModule(new ApplicationContextModule(this))
.repositoryModule(new RepositoryModule())
.build();
}
return applicationComponent;
}
在CreateActivity、EditActivity和MainActivity的onCreate方法中,Injector被替换为
((CustomApplication) getApplication())
.getOrCreateApplicationComponent()
.inject(this);
在RepositoryModule中,我们将使用Dagger2将依赖项注入构造函数中,这样我们就不需要手动注入上下文和DatabaseRealm
@Provides
@Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return new TradeRepositoryImpl(databaseRealm);
}
@Provides
@Singleton
public DatabaseRealm provideDatabaseRealm(Context context) {
return new DatabaseRealm(context);
}
然后在DatabaseRealm中,我们添加一个以上下文为参数的构造函数
Context mContext;
RealmConfiguration realmConfiguration;
public DatabaseRealm(Context context) {
mContext = context;
}
在TradeRepositoryImpl中,添加了一个带有databaseRealm的构造函数
DatabaseRealm databaseRealm;
public TradeRepositoryImpl(DatabaseRealm databaseRealm) {
this.databaseRealm = databaseRealm;
}
对于RepositoryTestModule,我们添加databaseRealm作为参数:
@Provides
@Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return isMocked ? mock(TradeRepository.class) : new TradeRepositoryImpl(databaseRealm);
}
在TestCustomApplication中,我们覆盖getOrCreateApplicationComponent
@Override
public ApplicationComponent getOrCreateApplicationComponent() {
return DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build();
}
现在,对于您的每个测试,我们使用RobolectricGradleTestRunner运行它们,并添加TestCustomApplication.class作为应用程序标记
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = TestCustomApplication.class)
当我们需要向测试中注入依赖项时,我们将如下注入:
@Before
public void setupDagger() {
DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build().inject(this);
}
EditActivityTest中仍然存在NullPointerException,因为此行:
loadTrade(intent.getExtras().getString("ID"));
要么检查意图是否为非空,要么在测试中提供一个意图。静态是邪恶的,powermock是邪恶的:) 我认为你应该离开你的班级。您不需要它,因为在应用程序的生命周期中只有一个CustomApplication对象 您应该按如下方式修改代码: 在CustomApplication.java中,创建应用程序组件,在字段变量中设置,然后注入
private ApplicationComponent applicationComponent;
public void setup(){
getOrCreateApplicationComponent().inject(this);
databaseRealm.setup();
stethoDebug.setup(this);
}
public ApplicationComponent getOrCreateApplicationComponent() {
if (applicationComponent == null) {
applicationComponent = DaggerApplicationComponent.builder()
.applicationContextModule(new ApplicationContextModule(this))
.repositoryModule(new RepositoryModule())
.build();
}
return applicationComponent;
}
在CreateActivity、EditActivity和MainActivity的onCreate方法中,Injector被替换为
((CustomApplication) getApplication())
.getOrCreateApplicationComponent()
.inject(this);
在RepositoryModule中,我们将使用Dagger2将依赖项注入构造函数中,这样我们就不需要手动注入上下文和DatabaseRealm
@Provides
@Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return new TradeRepositoryImpl(databaseRealm);
}
@Provides
@Singleton
public DatabaseRealm provideDatabaseRealm(Context context) {
return new DatabaseRealm(context);
}
然后在DatabaseRealm中,我们添加一个以上下文为参数的构造函数
Context mContext;
RealmConfiguration realmConfiguration;
public DatabaseRealm(Context context) {
mContext = context;
}
在TradeRepositoryImpl中,添加了一个带有databaseRealm的构造函数
DatabaseRealm databaseRealm;
public TradeRepositoryImpl(DatabaseRealm databaseRealm) {
this.databaseRealm = databaseRealm;
}
对于RepositoryTestModule,我们添加databaseRealm作为参数:
@Provides
@Singleton
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) {
return isMocked ? mock(TradeRepository.class) : new TradeRepositoryImpl(databaseRealm);
}
在TestCustomApplication中,我们覆盖getOrCreateApplicationComponent
@Override
public ApplicationComponent getOrCreateApplicationComponent() {
return DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build();
}
现在,对于您的每个测试,我们使用RobolectricGradleTestRunner运行它们,并添加TestCustomApplication.class作为应用程序标记
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = TestCustomApplication.class)
当我们需要向测试中注入依赖项时,我们将如下注入:
@Before
public void setupDagger() {
DaggerApplicationComponentTest.builder()
.applicationContextModuleTest(new ApplicationContextModuleTest())
.repositoryModuleTest(new RepositoryModuleTest(false))
.build().inject(this);
}
EditActivityTest中仍然存在NullPointerException,因为此行:
loadTrade(intent.getExtras().getString("ID"));
要么检查意图是否为非空,要么在测试中提供一个。查看您的测试类,我没有看到您使用任何Robolectric测试运行程序 您必须使用
RobolectricGradleTestRunner
或RobolectricTestRunner
触发Robolectric功能,以加载清单、解析资源、创建主循环器等
如果您不使用它们,您可能可以在设置中使用自己的代码来实现它,但这不是通常的方式,我不确定这里有多少人可以向您解释如何实现它
同样,Robolectric和PowerMock都在修改java
ClassLoader
。这就是为什么很难(也许不可能)让他们一起工作的原因。因此,请检查@Steve answer如何修改您的代码,以删除测试的必要性。查看您的测试类,我没有看到您使用任何Robolectric测试运行程序
您必须使用RobolectricGradleTestRunner
或RobolectricTestRunner
触发Robolectric功能,以加载清单、解析资源、创建主循环器等
如果您不使用它们,您可能可以在设置中使用自己的代码来实现它,但这不是通常的方式,我不确定这里有多少人可以向您解释如何实现它
同样,Robolectric和PowerMock都在修改java
ClassLoader
。这就是为什么很难(也许不可能)让他们一起工作的原因。因此,请检查@Steve answer如何修改您的代码以删除测试的必要性。您是否使用PowerMock
运行它?是的,正如您在这里看到的:是的,已经检查过了。“PowerMock”总是很棘手是的,这正是我发现的。看到一些人建议不要使用它,我想现在我真的需要它。你是否使用PowerMock
运行它?是的,正如你在这里看到的:是的,已经检查过了。“PowerMock”总是很棘手是的,这正是我发现的。看到一些人建议不要使用它,我想在这一点上我真的需要它。我通常创建Test
,它将由Robolectric自动加载,以避免定制测试应用程序
和