jam*_*ide 26 android dagger android-espresso
我正在尝试在Espresso仪器测试中设置Dagger,以模拟对外部资源的调用(在这种情况下为RESTful服务).我在Robolectric中为我的单元测试所遵循的模式是扩展我的生产Application类并使用将返回模拟的测试模块覆盖Dagger模块.我试图在这里做同样的事情,但是当我尝试将应用程序转换为我的自定义应用程序时,我在Espresso测试中遇到了ClassCastException.
这是我到目前为止的设置:
生产
在app/src/main/java/com/mypackage/injection下我有:
MyCustomApplication
package com.mypackage.injection;
import android.app.Application;
import java.util.ArrayList;
import java.util.List;
import dagger.ObjectGraph;
public class MyCustomApplication extends Application {
protected ObjectGraph graph;
@Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
List<Object> modules = new ArrayList<Object>();
modules.add(new AndroidModule(this));
modules.add(new RemoteResourcesModule(this));
modules.add(new MyCustomModule());
return modules;
}
public void inject(Object object) {
graph.inject(object);
}
}
Run Code Online (Sandbox Code Playgroud)
我用以下方式使用:
BaseActivity
package com.mypackage.injection.views;
import android.app.Activity;
import android.os.Bundle;
import com.mypackage.injection.MyCustomApplication;
public abstract class MyCustomBaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyCustomApplication)getApplication()).inject(this);
}
}
Run Code Online (Sandbox Code Playgroud)
被测活动
package com.mypackage.views.mydomain;
// imports snipped for bevity
public class MyActivity extends MyBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//snip
}
}
Run Code Online (Sandbox Code Playgroud)
浓缩咖啡设置
在app/src/androidTest/java/com/mypackage/injection下我有:
MyCustomEspressoApplication
package com.mypackage.injection;
import java.util.ArrayList;
import java.util.List;
import dagger.ObjectGraph;
public class MyCustomEspressoApplication extends MyCustomApplication {
private AndroidModule androidModule;
private MyCustomModule myCustomModule;
private EspressoRemoteResourcesModule espressoRemoteResourcesModule;
@Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
List<Object> modules = new ArrayList<Object>();
modules.add(getAndroidModule());
modules.add(getEspressoRemoteResourcesModule());
modules.add(getMyCustomModule());
return modules;
}
public void inject(Object object) {
graph.inject(object);
}
public AndroidModule getAndroidModule() {
if (this.androidModule == null) {
this.androidModule = new AndroidModule(this);
}
return this.androidModule;
}
public MyCustomModule getMyCustomModule() {
if (this.myCustomModule == null) {
this.myCustomModule = new MyCustomModule();
}
return this.myCustomModule;
}
public EspressoRemoteResourcesModule getEspressoRemoteResourcesModule() {
if (this.espressoRemoteResourcesModule == null) {
this.espressoRemoteResourcesModule = new EspressoRemoteResourcesModule();
}
return this.espressoRemoteResourcesModule;
}
}
Run Code Online (Sandbox Code Playgroud)
我的Espresso测试,在app/src/androidTest/com/mypackage/espresso下:
package com.mypackage.espresso;
// imports snipped for brevity
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity>{
private MyActivity myActivity;
public MyActivityTest() {
super(MyActivity.class);
}
@Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
myActivity = getActivity();
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testWhenTheActionBarButtonIsPressedThenThePlacesAreListed() {
//The next line is where the runtime exception occurs.
MyCustomEspressoApplication app = (MyCustomEspressoApplication)getInstrumentation().getTargetContext().getApplicationContext();
//I've also tried getActivity().getApplication() and
// getActivity.getApplicationContext() with the same results
//snip
}
}
Run Code Online (Sandbox Code Playgroud)
我的AndroidManifest.xml
(我之前在自定义应用程序类中看到了很多关于ClassCastException的答案,并且大多数都指向Application节点上缺少"android:name"属性.我在这里粘贴它表明事实并非如此据我所知.)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mypackage">
<!-- snip -->
<application
android:name=".injection.MyCustomApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- snip -->
</application>
<!-- snip -->
</manifest>
Run Code Online (Sandbox Code Playgroud)
的build.gradle
buildscript {
repositories {
mavenCentral()
jcenter()
}
}
apply plugin: 'com.android.application'
apply plugin: 'idea'
android {
testOptions {
unitTests.returnDefaultValues = true
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE'
}
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "com.mypackage"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
idea {
module {
testOutputDir = file('build/test-classes/debug')
}
}
dependencies {
compile project(':swipeablecardview')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-annotations:21.0.3'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.squareup:javawriter:2.5.0'
compile ('com.squareup.dagger:dagger:1.2.2') {
exclude module: 'javawriter'
}
compile ('com.squareup.dagger:dagger-compiler:1.2.2') {
exclude module: 'javawriter'
}
compile 'com.melnykov:floatingactionbutton:1.1.0'
compile 'com.android.support:cardview-v7:21.0.+'
compile 'com.android.support:recyclerview-v7:21.0.+'
// compile 'se.walkercrou:google-places-api-java:2.1.0'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
compile 'commons-io:commons-io:1.3.2'
testCompile 'org.hamcrest:hamcrest-integration:1.3'
testCompile 'org.hamcrest:hamcrest-core:1.3'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile('junit:junit:4.12')
testCompile 'org.mockito:mockito-core:1.+'
testCompile('org.robolectric:robolectric:3.0-SNAPSHOT')
testCompile('org.robolectric:shadows-support-v4:3.0-SNAPSHOT')
androidTestCompile 'org.mockito:mockito-core:1.+'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.0') {
exclude group: 'javax.inject'
exclude module: 'javawriter'
}
androidTestCompile('com.android.support.test:testing-support-lib:0.1')
}
Run Code Online (Sandbox Code Playgroud)
堆栈跟踪:
java.lang.ClassCastException:com.mypackage.injection.MyCustomApplication无法强制转换为com.mypackage.injection.MyCustomEspressoApplication,位于java.lang.reflect.Method的com.mypackage.espresso.MyActivityTest.testWhenTheActionBarButtonIsPressedThenThePlacesAreListed(MyActivityTest.java:107).在org.junit.internal.runners的org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall(FrameworkMethod.java:45)的java.lang.reflect.Method.invoke(Method.java:511)中的invokeNative(Native Method) .model.ReflectiveCallable.run(ReflectiveCallable.java:15)org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java) :20)org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)org.junit上的org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30). runners.ParentRunner.runLeaf(ParentRunner.java:263)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)org.junit.runners.ParentRunner $ 3.run(ParentRunner.java:231)org.junit.runners.ParentRunner $ 1 .schedule(ParentRunner.java:60)org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:50)org.junit.runners .parentRunner $ 2.evaluate(ParentRunner.java:222)org.junit.runners.ParentRunner.run(ParentRunner.java:300)atg.junit.runners.Suite.runChild(Suite.java:128)org.junit .runners.Suite.runChild(Suite.java:24)org.junit.runners.ParentRunner $ 3.run(ParentRunner.java:231)at org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:60)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:50)org.junit.runners.ParentRunner $ 2.evaluate(ParentRunner.java: 222)在org.junit.runners.ParentRunner.run(ParentRunner.java:300)org.junit.runner.JUnitCore.run(JUnitCore.java:157)org.junit.runner.JUnitCore.run(JUnitCore.java:136)at android.support.test.runner.AndroidJUnitRunner.onStart (AndroidJUnitRunner.java:270)在android.app.Instrumentation $ InstrumentationThread.run(Instrumentation.java:1551)
我已经阅读了Espresso和Dagger文档,并通过Github上的问题搜索无济于事.我很感激任何人都可以提供帮助.提前致谢.
编辑#1
我遵循Daniel的建议来扩展测试运行器并检查VerifyError,并获得以下堆栈跟踪:
java.lang.ExceptionInInitializerError
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at com.mypackage.injection.EspressoRemoteResourcesModule.<init>(EspressoRemoteResourcesModule.java:17)
at com.mypackage.injection.MyCustomEspressoApplication.getEspressoRemoteResourcesModule(MyCustomEspressoApplication.java:52)
at com.mypackage.injection.MyCustomEspressoApplication.getModules(MyCustomEspressoApplication.java:24)
at com.mypackage.injection.MyCustomApplication.onCreate(MyCustomApplication.java:18)
at com.mypackage.injection.MyCustomEspressoApplication.onCreate(MyCustomEspressoApplication.java:16)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:999)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4151)
at android.app.ActivityThread.access$1300(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1255)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at com.mypackage.injection.EspressoRemoteResourcesModule.<init>(EspressoRemoteResourcesModule.java:17)
at com.mypackage.injection.MyCustomEspressoApplication.getEspressoRemoteResourcesModule(MyCustomEspressoApplication.java:52)
at com.mypackage.injection.MyCustomEspressoApplication.getModules(MyCustomEspressoApplication.java:24)
at com.mypackage.injection.MyCustomApplication.onCreate(MyCustomApplication.java:18)
at com.mypackage.injection.MyCustomEspressoApplication.onCreate(MyCustomEspressoApplication.java:16)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:999)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4151)
at android.app.ActivityThread.access$1300(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1255)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
04-29 06:40:28.594 1016-1016/? W/ActivityManager? Error in app com.mypackage running instrumentation ComponentInfo{com.mypackage.test/com.mypackage.EspressoTestRunner}:
04-29 06:40:28.594 1016-1016/? W/ActivityManager? java.lang.VerifyError
04-29 06:40:28.594 1016-1016/? W/ActivityManager? java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
Run Code Online (Sandbox Code Playgroud)
这使我指向Mockito.我错过了必要的mockito和dexmaker库.
我将我的依赖项更新为:
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile ('com.google.dexmaker:dexmaker-mockito:1.2') {
exclude module: 'hamcrest-core'
exclude module: 'mockito-core'
}
androidTestCompile('com.android.support.test.espresso:espresso-core:2.0') {
exclude group: 'javax.inject'
}
Run Code Online (Sandbox Code Playgroud)
我还覆盖了MyCustomModule,它需要包含EspressoRemoteResourcesModule.一旦我做了这件事,就开始工作了.
Dan*_*rov 34
使用自定义检测运行器,您可以覆盖newApplication并使其从清单中实例化除默认应用程序之外的其他内容.
public class MyRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(ClassLoader cl, String className, Context context)
throws Exception {
return super.newApplication(cl, MyCustomEspressoApplication.class.getName(), context);
}
}
Run Code Online (Sandbox Code Playgroud)
请务必testInstrumentationRunner使用您的自定义跑步者名称进行更新.
Xi *_*Wei 21
花了我一整天才得到完整的答案.
第1步:覆盖AndroidJUnitRunner
public class TestRunner extends AndroidJUnitRunner
{
@Override
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, TestApplication.class.getName(), context);
}
}
Run Code Online (Sandbox Code Playgroud)
第2步:替换build.gradle中现有的AndroidJunitRunner
defaultConfig {
...
// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner 'com.xi_zz.androidtest.TestRunner'
}
Run Code Online (Sandbox Code Playgroud)
第3步:添加com.android.support.test:runner到build.gradle
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
Run Code Online (Sandbox Code Playgroud)
第4步:仅当您收到此错误时
Warning:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (25.2.0) and test app (23.1.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.
Run Code Online (Sandbox Code Playgroud)
然后,再添加一行:
androidTestCompile 'com.android.support:support-annotations:25.2.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
Run Code Online (Sandbox Code Playgroud)
最后,测试它是否有效
@RunWith(AndroidJUnit4.class)
public class MockApplicationTest
{
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
@Test
public void testApplicationName() throws Exception
{
assertEquals("TestApplication", mActivityRule.getActivity().getApplication().getClass().getSimpleName());
}
}
Run Code Online (Sandbox Code Playgroud)
如果要测试库模块,则可以制作一个自定义应用程序类并将其注册在测试包清单中:
根目录/库模块/src/androidTest/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:name="path.to.TestApplication" />
</manifest>
Run Code Online (Sandbox Code Playgroud)
没有规则,没有赛跑者。
我没有在所有情况下都尝试过这种方法,但是您可以尝试使用自定义规则为每个测试用例指定自定义应用程序类,而不是为自定义运行程序应用的所有测试用例指定自定义应用程序类。在简单的情况下,我在以下方面取得了成功:
public class ApplicationTestRule<T extends Application> extends UiThreadTestRule {
Class<T> appClazz;
boolean wait = false;
T app;
public ApplicationTestRule(Class<T> applicationClazz) {
this(applicationClazz, false);
}
public ApplicationTestRule(Class<T> applicationClazz, boolean wait) {
this.appClazz = applicationClazz;
this.wait = wait;
}
@Override
public Statement apply(final Statement base, Description description) {
return new ApplicationStatement(super.apply(base, description));
}
private void terminateApp() {
if (app != null) {
app.onTerminate();
}
}
public void createApplication() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
app = (T) InstrumentationRegistry.getInstrumentation().newApplication(this.getClass().getClassLoader(), appClazz.getName(), InstrumentationRegistry.getInstrumentation().getTargetContext());
InstrumentationRegistry.getInstrumentation().callApplicationOnCreate(app);
}
private class ApplicationStatement extends Statement {
private final Statement mBase;
public ApplicationStatement(Statement base) {
mBase = base;
}
@Override
public void evaluate() throws Throwable {
try {
if (!wait) {
createApplication();
}
mBase.evaluate();
} finally {
terminateApp();
app = null;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在您的测试用例中,创建规则:
@Rule
public ApplicationTestRule<TestApplication> appRule = new ApplicationTestRule<>(TestApplication.class,true);
Run Code Online (Sandbox Code Playgroud)
注意第二个参数是可选的。如果为false或不设置,则每次在每个测试用例之前创建自定义应用程序。如果设置为true,则需要appRule.createApplication()在应用程序逻辑之前调用。
| 归档时间: |
|
| 查看次数: |
8518 次 |
| 最近记录: |