0

Recently I've been unit testing doing an update using the latest android studio, which is 3 beta 6, and I've found that even when I initialize a ContentValues it is null. I need it to actually be a value / init-ed ContentValues.

my gradle file:

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'
}

but wait, there's more!

So I've also got a simple test which is:

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!
    }
}

here is the view in android studio android beta showing null for content values

So I've tried a default gradle file and found that contentvalues is not null.

here is the file:

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'
}

I honestly don't understand gradle a lot at this point, but I do know that my config, which is a bunch of ideas to support dagger and other values is not great by any stretch.

After trying other things, I found this to be the culprit:

testOptions {
    unitTests.returnDefaultValues = true
}

I figured out the reason why I had this. I have a test in another area that is testing intents, and I need the default.

Is there a way to restrict this to exclude ContentValues or the inverse to not include the Intent?

KellyTheDev
  • 891
  • 2
  • 12
  • 31

2 Answers2

1

Well, it's solved. At the time of this writing, you still cannot do unit testing against android content classes like ContentValues and Intents.

Because of this, I've have introduced Robolectric. It's not difficult, but here are the changes.

the gradle file:

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

The test:

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);
    }
}
KellyTheDev
  • 891
  • 2
  • 12
  • 31
0

You need to mock the constructor, which can be accomplished with PowerMock:

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

This happens because Android has not been instantiated when your test begins - you are only running plain Java at that point, which helps these unit tests run very fast.

Keep in mind if you are trying to call ContentValues() in the actual source code, you must also prepare the class where the constructor is called, as is explained here:

@PrepareForTest({ClassWhereContentValuesIsInitialized.class})

Stepping back a second, using Robolectric is an excellent tool for UI end-to-end testing, but it doesn't currently allow complex mocking like Mockito/Powermock does. Both frameworks are used for totally different purposes, and both can be used in tandem - but just switching to Robolectric isn't technically a solution to the problem posed.

Granted, Mockito is painful to setup for Android since so much boilerplate Android logic usually has to be mocked out, and some might argue complex logic shouldn't live completely in client-side Android code anyways, or argue against unit testing in general. Still, it might be the right tool for your use case.

jbass
  • 116
  • 1
  • 8