伸出援助之手,因为我一直在以前从未见过的问题撞墙。
我有一个标准的片段活动,使用浓缩咖啡进行测试。我在应用程序链接的关联模块库中有一个静态管理器。
问题:
当测试运行时,它会对我的ServiceManager进行静态调用。当代码在onCreateView中执行时,在堆栈跟踪中,它在管理器中调用一个不同的方法,而不是它应该的方法。更奇怪的是,传递的参数在追踪时毫无意义。代码如下(简化项目以说明正在发生的事情)。
ServiceManager.java - 来自项目库
public class ServiceManager {
private static final ServiceManager INSTANCE = new ServiceManager();
private UserManager userManager;
public static initForTest(Context context) {
// some storage initialization here
INSTANCE.userManager = new UserManagerImpl();
}
public static ServiceManager getInstance() {
return INSTANCE;
}
public static UserManager getUserManager() {
return INSTANCE.userManager;
}
}
UserManager.java - 来自项目库
public interface UserManager {
void registerListener(UserListener listener);
void refreshDataFromSource(String id);
}
UserManagerImpl.java - 来自项目库
public class UserManagerImpl implements UserManager {
public void registerListener(UserListener listener) {
// code really does not matter here, but I have a listener manager
ManagerListeners.register(UserListener.class, this);
}
public void refreshDataFromSource(String id) {
// kicks off an async task of mine
Log.i(TAG, "initiating refresh for %s", id);
UserRefreshTask task = new UserRefreshTask(
new TaskCallbackWithReturn<Consumer>() {
@Override
public void done(Consumer consumer) {
refreshAllListeners(true);
}
@Override
public void fail() {
refreshAllListeners(false);
}
}, id);
task.execute();
}
}
NewUserStartFragment.java - 在应用项目中
public class UserProfileStartFragment extends Fragment implements UserListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Get the view from fragment_newuserstart.xml
View view = inflater.inflate(R.layout.fragment_newuserstart, container, false);
// register listeners
ServiceManager.getInstance().getUserManager().registerListener(this);
// rest does not matter since we don't get to it.
return view;
}
}
来自应用项目的支持(死简单)活动 NewUserActivity.java
public class NewUserActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_newuser);
if (savedInstanceState == null) {
FragmentUtils.replace(this, R.id.fragment_userscreen, new UserProfileStartFragment());
}
Log.i(TAG, "Activity created");
}
}
一个简单的测试 NewUserStartFragmentTest.java - 在app项目中测试
@RunWith(AndroidJUnit4.class)
public class UserProfileStartFragmentTest {
// on activity rule, activity initialization delayed on false
@Rule
public ActivityTestRule<NewUserActivity> mActivityRule =
new ActivityTestRule<>(NewUserActivity.class, true, false);
private NewUserActivity userActivity;
@Before
public void setUp() throws Exception {
Instrumentation instrumentation
= InstrumentationRegistry.getInstrumentation();
Context context = instrumentation.getTargetContext();
// service manager at this point will be initialized
ServiceManager.initForTest(context);
}
@After
public void tearDown() throws Exception {
// ensure activity is dead
ActivityUtils.finish(userActivity);
ServiceManager.reset();
}
@Test
public void testInitialScreen() throws Exception {
userActivity = mActivityRule.launchActivity(new Intent());
onView(withId(R.id.image_profile_photo)).check(matches(notNullValue()));
onView(withId(R.id.button_email)).check(matches(notNullValue()));
onView(withId(R.id.button_existing)).check(matches(notNullValue()));
onView(withId(R.id.button_cancel)).check(matches(notNullValue()));
}
}
现在好奇
测试运行时,它总是报告以下内容。
java.lang.IllegalArgumentException: consumerId must not be null or empty
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:122)
at com.arryved.android.applibrary.tasks.UserRefreshTask.<init>(UserRefreshTask.java:31)
**at com.arryved.android.applibrary.manager.usermanager.UserManagerImpl.refreshDataFromSource(UserManagerImpl.java:300)
at com.arryved.emptor.fragments.UserProfileStartFragment.onCreateView(UserProfileStartFragment.java:41)**
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1965)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1078)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1259)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1624)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:330)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:547)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1220)
at android.support.test.runner.MonitoringInstrumentation.callActivityOnStart(MonitoringInstrumentation.java:546)
at android.app.Activity.performStart(Activity.java:5953)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2261)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
UM,它不应该执行refreshDataFromSource。它应该执行registerListener。
因此,我决定添加一个停止点并进行调试。更奇怪的是,它正在执行代码,但不传递字符串,而是将片段作为参数传递。单独使用Java变量应该会导致失败!这告诉我它必须是一个字节代码混淆。它是dex吗?还有别的吗?我以前从未见过这样的事。
我曾尝试过MacOs android studio和Linux android studio。我已经完成了从命令行和工作室清理的构建。我遇到过几种不同的仿真器和一台实际的Nexus 5设备。没关系,相同的结果告诉我在构建之后它会混淆,而不是因为代码。
哦,还有另一个参考点。只需运行应用程序,片段就可以正常工作。当我刚刚运行测试时,这似乎正在发生。 (但我担心在dex中还有其他一些险恶的东西,所以我不相信应用程序,除非我在测试中弄明白这一点。)
以下是该应用的 build.gradle 。
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.arryved.emptor"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// Enabling multidex support.
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
}
dexOptions {
javaMaxHeapSize "4g"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:recyclerview-v7:23.1.0'
androidTestCompile 'com.android.support.test:runner:0.4'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.1') {
exclude group: 'com.google.guava', module: 'guava'
}
// add this for intent mocking support
androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2') {
exclude group: 'com.google.guava', module: 'guava'
}
// add this for webview testing support
androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.1') {
exclude group: 'com.google.guava', module: 'guava'
}
compile project(':AppLibrary')
}
我的 AppLibrary build.gradle
apply plugin: 'com.android.library'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
}
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// Enabling multidex support.
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
dexOptions {
javaMaxHeapSize "4g"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:multidex:1.0.0'
compile("com.google.android.gms:play-services:8.3.0") {
exclude group: 'com.android.support', module: 'support-v4'
}
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:recyclerview-v7:23.1.0'
androidTestCompile 'com.android.support.test:runner:0.4'
androidTestCompile 'com.android.support.test:rules:0.4'
}
-------------- UPDATE ---------------
只是看看会发生什么,我更新了服务管理器来处理硬实现(UserManagerImpl)而不是接口(UserManager)。使用实现而不是接口引用时,测试通过。因此,在这一点上,我能确定的最好的是,在链接周期中,android或dex对界面感到困惑。当我将其更改回来时,测试又恢复为失败,如上所示(因此也不是随机构建问题)。