[英]Context and Resource in Android Instrumented Unit Tests
我正在設計一個系統,其中包含一些不太簡單的類,需要Context對象才能初始化它們。 這些類使用第三方類,這些類也需要上下文初始化。 此類還利用上下文來加載功能所需的許多字符串資源。
問題在於為這些類編寫Instrumented Unit測試。 當我嘗試使用InstrumentationRegistry.getContext()為測試獲取Context對象時,我遇到了一個異常,其中上下文找不到與該類關聯的字符串資源(android.content.res.Resources $ NotFoundException)。
我的問題是:我如何設計這些測試,以便上下文可以檢索我需要的字符串資源,還可以作為第三方類的合適上下文對象? 我只能做很多嘲弄,因為其中一些類處理auth令牌,這很難模仿。 我不可能是唯一一個在Android域中遇到此問題的人,所以我確信這個可能是常見問題的解決方案。
編輯:正如所建議的,我已經嘗試在我的項目中集成Robolectric(版本3.3.2),但是當我嘗試運行我的單元測試時,我遇到了以下錯誤:
Error:Error converting bytecode to dex:
Cause: Dex cannot parse version 52 byte code.
This is caused by library dependencies that have been compiled using Java 8 or above.
If you are using the 'java' gradle plugin in a library submodule add
targetCompatibility = '1.7'
sourceCompatibility = '1.7'
to that submodule's build.gradle file.
我嘗試將targetCompatibility和sourceCompatibility行添加到我的gradle文件(在幾個位置)無濟於事。
這是我的移動build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'checkstyle'
apply plugin: 'io.fabric'
project.ext {
supportLibVersion = '25.3.0'
multiDexSupportVersion = '1.0.1'
gsonVersion = '2.8.0'
retrofitVersion = '2.2.0'
daggerVersion = '2.4'
butterKnifeVersion = '8.5.1'
eventBusVersion = '3.0.0'
awsCoreServicesVersion = '2.2.+'
twitterKitVersion = '2.3.2@aar'
facebookVersion = '4.+'
crashlyticsVersion = '2.6.7@aar'
autoValueVersion = '1.2'
autoValueParcelVersion = '0.2.5'
autoValueGsonVersion = '0.4.4'
permissionDispatcher = '2.2.0'
testRunnerVersion = '0.5'
espressoVersion = '2.2.2'
junitVersion = '4.12'
roboelectricVersion = '3.3.2'
}
def gitSha = exec('git rev-parse --short HEAD', "unknown");
def gitCommitCount = 100 + Integer.parseInt(exec('git rev-list --count HEAD', "-1"))
def gitTag = exec('git describe --tags', stringify(gitCommitCount))
def gitTimestamp = exec('git log -n 1 --format=%at', -1)
def appId = "com.example.myapp"
def isCi = "true".equals(System.getenv("CI"))
// Uncomment if you wish to enable Jack & Java8
// apply from: 'jack.gradle'
// Uncomment if you wish to enable Sonar
//apply from: 'sonar.gradle'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId appId
minSdkVersion 16
targetSdkVersion 25
multiDexEnabled = true
versionCode gitCommitCount
versionName gitTag
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField 'String', 'GIT_SHA', "\"${gitSha}\""
buildConfigField 'long', 'GIT_TIMESTAMP', "${gitTimestamp}L"
}
buildTypes {
debug {
applicationIdSuffix '.debug'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
qa.initWith(buildTypes.release)
qa {
applicationIdSuffix '.qa'
debuggable true
}
}
lintOptions {
abortOnError false
}
applicationVariants.all { variant ->
def strictMode = !variant.name.equals("release")
buildConfigField 'boolean', 'STRICT_MODE_ENABLED', "${strictMode}"
}
}
configurations.all {
resolutionStrategy {
force "com.android.support:support-annotations:$supportLibVersion"
force "com.squareup.okhttp3:okhttp:3.4.1"
force "com.squareup:okio:1.9.0"
force "com.google.guava:guava:19.0"
}
}
dependencies {
compile "com.android.support:appcompat-v7:$supportLibVersion"
compile "com.android.support:design:$supportLibVersion"
compile "com.android.support:recyclerview-v7:$supportLibVersion"
compile "com.android.support:cardview-v7:$supportLibVersion"
compile "com.android.support:multidex:$multiDexSupportVersion"
compile "com.squareup.retrofit2:retrofit:$retrofitVersion"
compile "com.squareup.retrofit2:converter-gson:$retrofitVersion"
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile "com.google.dagger:dagger:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.jakewharton.timber:timber:4.3.1'
compile "com.jakewharton:butterknife:$butterKnifeVersion"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnifeVersion"
compile "org.greenrobot:eventbus:$eventBusVersion"
annotationProcessor "org.greenrobot:eventbus:$eventBusVersion"
compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
debugCompile 'com.squareup.okhttp3:logging-interceptor:3.4.2'
compile "com.google.auto.value:auto-value:$autoValueVersion"
annotationProcessor "com.google.auto.value:auto-value:$autoValueVersion"
compile "com.ryanharter.auto.value:auto-value-parcel-adapter:$autoValueParcelVersion"
annotationProcessor "com.ryanharter.auto.value:auto-value-parcel:$autoValueParcelVersion"
compile "com.github.hotchemi:permissionsdispatcher:$permissionDispatcher"
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcher"
compile("com.crashlytics.sdk.android:crashlytics:$crashlyticsVersion") {
transitive = true;
}
compile("com.twitter.sdk.android:twitter:$twitterKitVersion") {
transitive = true
}
compile "com.facebook.android:facebook-android-sdk:$facebookVersion"
compile "com.amazonaws:aws-android-sdk-core:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-core:$awsCoreServicesVersion"
compile "com.amazonaws:aws-android-sdk-apigateway-core:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-apigateway-core:$awsCoreServicesVersion"
compile "com.amazonaws:aws-android-sdk-cognito:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-cognito:$awsCoreServicesVersion"
compile "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$awsCoreServicesVersion"
compile "com.amazonaws:aws-android-sdk-lambda:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-lambda:$awsCoreServicesVersion"
compile "com.amazonaws:aws-android-sdk-sns:$awsCoreServicesVersion"
annotationProcessor "com.amazonaws:aws-android-sdk-sns:$awsCoreServicesVersion"
androidTestCompile "junit:junit:$junitVersion"
androidTestCompile "com.android.support.test:runner:$testRunnerVersion"
androidTestCompile "com.android.support.test:rules:$testRunnerVersion"
androidTestCompile "com.android.support.test.espresso:espresso-intents:$espressoVersion"
androidTestCompile "com.android.support.test.espresso:espresso-core:$espressoVersion"
androidTestCompile "com.squareup.retrofit2:retrofit-mock:$retrofitVersion"
androidTestCompile "org.robolectric:robolectric:$roboelectricVersion"
testCompile "junit:junit:$junitVersion"
testCompile 'com.google.truth:truth:0.30'
testCompile 'org.hamcrest:hamcrest-all:1.3'
testCompile "org.robolectric:robolectric:$roboelectricVersion"
}
task checkCodingStyle(type: Checkstyle) {
description 'Runs Checkstyle inspection against Android sourcesets.'
group = 'Code Quality'
ignoreFailures = false
showViolations = false
source 'src'
include '**/*.java'
exclude '**/gen/**'
exclude '**/R.java'
exclude '**/BuildConfig.java'
reports {
xml.destination "$project.buildDir/reports/checkstyle/report.xml"
}
classpath = files()
configFile = file("${rootProject.rootDir}/config/checkstyle/checkstyle.xml")
}
def stringify(int versionCode) {
def builder = new StringBuilder();
def dot = ""
String.format("%03d", versionCode).toCharArray().each {
builder.append(dot)
builder.append(it)
dot = "."
}
return builder.toString()
}
def exec(String command, Object fallback = null) {
def cmd = command.execute([], project.rootDir)
cmd.waitFor()
if (cmd.exitValue() != 0) {
if (fallback == null) {
throw new RuntimeException("'$command' failed: $cmd.errorStream.text")
} else {
return fallback
}
}
return cmd.text.trim()
}
if (isCi) {
build.finalizedBy(checkCodingStyle)
}
接受的答案不是實際的解決方案。 在許多情況下,您希望測試與真實Android框架的交互。 與任何其他存根一樣,Robolectric可能會隱藏一些實際問題。
您的問題是您使用的InstrumentationRegistry.getContext()
與您的應用程序使用的不同。 根據文件:
返回此檢測包的Context。
你應該使用InstrumentationRegistry.getTargetContext()
代替:
返回正在檢測的目標應用程序的上下文。
因為它與第一個相反,可以訪問您的資源。
Robolectric可能是解決您問題的方法。 它允許您在test
文件夾中編寫單元測試(注意: androidTest
文件夾中沒有檢測的單元測試),這些單元測試利用Context訪問所有R.string.*
資源。
這是一個例子:
@RunWith(RobolectricTestRunner.class)
public class FooTest {
private final Contet context;
@Before
public void setup() {
context = RuntimeEnvironment.application;
}
public void testMyStringResource() {
String mySpecialString = context.getString(R.string.mySpecialString);
assertEquals("special", mySpecialString);
}
}
更新:
您從Robolectric獲得的錯誤消息是由將依賴項作為androidTestCompile
而不是testCompile
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.