mardi 20 mars 2018

How to unit test a Android Library?

I can't figure out the correct setup that I need to let my unit tests run correctly inside my android library.

My application consists of a 4 modules: 1 application and 3 android libraries. I want to run a unit test inside one of my libraries and I always get this error:

java.lang.IllegalStateException: Failed to transform class with name ch.leonardbutz.hellosunshine.core.controller.DataHandlerTest. Reason: cannot find android.content.Context

    at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:296)
    at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:204)
    at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass1(DeferSupportingClassLoader.java:89)
    at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:79)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:340)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:161)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:48)
    at org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl.createTestDelegators(AbstractTestSuiteChunkerImpl.java:108)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.<init>(JUnit4TestSuiteChunkerImpl.java:71)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.<init>(AbstractCommonPowerMockRunner.java:36)
    at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:34)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
    at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Caused by: javassist.CannotCompileException: cannot find android.content.Context
    at javassist.expr.NewExpr.replace(NewExpr.java:215)
    at org.powermock.core.transformers.impl.AbstractMainMockTransformer$PowerMockExpressionEditor.edit(AbstractMainMockTransformer.java:500)
    at javassist.expr.ExprEditor.loopBody(ExprEditor.java:212)
    at javassist.expr.ExprEditor.doit(ExprEditor.java:91)
    at javassist.CtClassType.instrument(CtClassType.java:1465)
    at org.powermock.core.transformers.impl.ClassMockTransformer.transformMockClass(ClassMockTransformer.java:65)
    at org.powermock.core.transformers.impl.AbstractMainMockTransformer.transform(AbstractMainMockTransformer.java:62)
    at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:277)
    ... 31 more
Caused by: javassist.NotFoundException: android.content.Context
    at javassist.ClassPool.get(ClassPool.java:452)
    at javassist.bytecode.Descriptor.toCtClass(Descriptor.java:592)
    at javassist.bytecode.Descriptor.getParameterTypes(Descriptor.java:439)
    at javassist.expr.NewExpr.replace(NewExpr.java:188)
    ... 38 more

I don't quite understand this, because in my opinion I have everything declared correctly in my dependencies...

gradle.build:

apply plugin: 'com.android.library'

android {
    compileSdkVersion project.myCompileSdkVersion

    defaultConfig {
        minSdkVersion project.myMinSdkVersion
        targetSdkVersion project.myTargetSdkVersion
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

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

    lintOptions {
        abortOnError false
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    testCompile 'junit:junit:4.12'
    testCompile 'org.powermock:powermock-api-mockito:1.7.3'
    testCompile 'org.powermock:powermock-module-junit4:1.7.3'

    implementation project(':hellosunshine-communication')
    compileOnly project(':hellosunshine-model')
}

And my test class:

package ch.leonardbutz.hellosunshine.core.controller;

import android.content.Context;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import ch.leonardbutz.hellosunshine.core.communication.weather.OpenWeatherDataService;
import ch.leonardbutz.hellosunshine.core.data.source.cache.FileWeatherCachingService;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DataHandler.class, FileWeatherCachingService.class, OpenWeatherDataService.class})
public class DataHandlerTest {

    private DataHandler dataHandler;

    @Mock
    private Context contextMock;

    @Mock
    private FileWeatherCachingService fileWeatherCachingServiceMock;

    @Mock
    private OpenWeatherDataService openWeatherDataServiceMock;

    @BeforeClass
    public void setupSuite() throws Exception {
        PowerMockito.whenNew(FileWeatherCachingService.class).withNoArguments().thenReturn(fileWeatherCachingServiceMock);
        PowerMockito.whenNew(OpenWeatherDataService.class).withNoArguments().thenReturn(openWeatherDataServiceMock);
    }

    @Before
    public void setupTest() {
        dataHandler = new DataHandler(contextMock);
    }

    @Test
    public void testLoadWeather_withCachedData() {
        // TODO
        Assert.assertEquals(true, true);
    }

}

Any help would be really appreciated. The whole project can also be found here: https://github.com/ControlTheBit/hello-sunshine

Aucun commentaire:

Enregistrer un commentaire