如何使用 ContentValues 始终为 null 与 android.content.Intent 中的 Method getAction 进行单元测试未被模拟

How to unit test with ContentValues always null vs Method getAction in android.content.Intent not mocked

最近我一直在使用最新的 android studio 进行单元测试,它是 3 beta 6,我发现即使我初始化 ContentValues 它也是空的.我需要它实际上是一个值/初始化 ContentValues.

我的gradle文件:

apply plugin: 'com.android.application'

android {

    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.cubedelement.soundplanner"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "org.mockito.testInstrumentationRunner"
        vectorDrawables.useSupportLibrary = true
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }

    testOptions {
        unitTests.returnDefaultValues = true
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:palette-v7:26.1.0'
    androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.0', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

    implementation 'com.google.dagger:dagger-android:2.11'
    implementation 'com.google.dagger:dagger-android-support:2.11' // if you use the support libraries
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test:rules:1.0.1'
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.google.code.gson:gson:2.8.0'
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.powermock:powermock-api-mockito2:1.7.0'
    testImplementation 'org.powermock:powermock-module-junit4:1.6.5'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'org.mockito:mockito-core:2.8.47'
    androidTestImplementation 'org.mockito:mockito-core:2.8.47'
}

等等,还有更多!

所以我还有一个简单的测试是:

import android.content.ContentValues;

public class Test {
    @Test public void ContentValuesShouldNotBeNull(){
         ContentValues v = new ContentValues();
         assertEquals(v, null) // this is true, but I don't want it to be, help!
    }
}

这是android工作室的景色

所以我尝试了一个默认的 gradle 文件,发现 contentvalues 不为空。

这是文件:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion '26.0.1'
    defaultConfig {
        applicationId "com.example.kelly_vernon.myapplication"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

老实说,目前我不太了解 gradle,但我知道我的配置(这是一堆支持匕首和其他值的想法)无论如何都不是很好。

尝试其他方法后,我发现这是罪魁祸首:

testOptions {
    unitTests.returnDefaultValues = true
}

我想通了我有这个的原因。我在另一个测试意图的区域进行测试,我需要默认值。

有没有办法限制它排除 ContentValues 或相反不包括 Intent

好了,解决了。在撰写本文时,您仍然无法对 android 内容 类 进行单元测试,例如 ContentValuesIntents.

因此,我介绍了Robolectric。这并不难,但这里有一些变化。

gradle 文件:

testOptions {
    unitTests {
        returnDefaultValues = true
        includeAndroidResources = true // new line
    }
}

测试:

import android.content.ContentValues;

@RunWith(RobolectricTestRunner.class) //new line to start robolectric
public class Test {
    @Test public void ContentValuesShouldNotBeNull(){
         ContentValues v = new ContentValues();
         assertNotNull(v, null);
    }
}

您需要模拟构造函数,这可以通过 PowerMock 完成:

PowerMockito.whenNew(ContentValues.class).withNoArguments()
             .thenReturn(mockContentValues);`

发生这种情况是因为当您的测试开始时 Android 尚未实例化 - 那时您只是 运行ning 普通 Java,这有助于这些单元测试 运行 很快。

请记住,如果您尝试在实际源代码中调用 ContentValues(),您还必须准备调用构造函数的 class,如 here 所述:

@PrepareForTest({ClassWhereContentValuesIsInitialized.class})

退后一步,使用 Robolectric 是 UI end-to-end 测试的绝佳工具,但它目前不允许像 Mockito/Powermock 那样的复杂模拟。这两个框架用于完全不同的目的,并且都可以串联使用 - 但仅切换到 Robolectric 从技术上讲并不能解决所提出的问题。

诚然,为 Android 设置 Mockito 很痛苦,因为通常必须模拟出如此多的样板 Android 逻辑,有些人可能认为复杂的逻辑不应该完全存在于 client-side Android 代码,或者反对一般的单元测试。不过,它可能是适合您的用例的工具。