2016-02-17 26 views
5

Sto cercando di usare PowerMock per deridere una classe con un metodo statico, ma in particolare desidero farlo all'interno di un Test di strumentazione Android. Per essere chiari vorrei eseguire il test su un vero dispositivo Android o su un emulatore. Utilizzo Android Studio (1.5.1) e Gradle (1.5.0). Per evitare aringhe rosse ho creato un'applicazione per l'hello world davvero semplice e piuttosto rozza. Questa applicazione mostra semplicemente 2 pezzi di testo, uno recuperato da un metodo statico e uno da un metodo non statico. Ho scritto test di strumentazione per entrambe queste classi di 'provider di testi'. Si può vedere questa applicazione qui:Utilizzo di PowerMock e Mockito in un test di Strumentazione Android - Errore - File duplicati - org.mockito.plugins.MockMaker

https://github.com/Kai2k/PowerMockAndroidTest.git

Quando si tenta di eseguire i test di strumentazione ottengo l'errore che compare molte persone che cercano di raggiungere questo get:

com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK mockito-extensions/org.mockito.plugins.MockMaker 
File1: /Users/kaiarmer/.gradle/caches/modules-2/files-2.1/com.crittercism.dexmaker/dexmaker-mockito/1.4/70892a94894462c1b35df3c8a77d21b7e843550b/dexmaker-mockito-1.4.jar 
File2: /Users/kaiarmer/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-api-mockito/1.6.4/fe12509b7e9e49d25131f4155145748a31e42e40/powermock-api-mockito-1.6.4.jar 

mie dipendenze sulle mie app di aspetto di file build.gradle come questo:

dependencies { 
compile fileTree(include: ['*.jar'], dir: 'libs') 
testCompile 'junit:junit:4.12' 
testCompile 'org.powermock:powermock-api-mockito:1.6.4' 
testCompile 'org.powermock:powermock-module-junit4-rule-agent:1.6.4' 
testCompile 'org.powermock:powermock-module-junit4-rule:1.6.4' 
testCompile 'org.powermock:powermock-module-junit4:1.6.4' 

androidTestCompile 'org.mockito:mockito-core:1.10.19' 
androidTestCompile 'org.powermock:powermock-api-mockito:1.6.4' 
androidTestCompile 'com.android.support:support-annotations:23.1.1' 
androidTestCompile 'com.android.support.test:runner:0.4.1' 
androidTestCompile 'com.android.support.test:rules:0.4.1' 
androidTestCompile 'org.hamcrest:hamcrest-library:1.3' 
androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4' 
androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.4' 

compile 'com.android.support:appcompat-v7:23.1.1' 
compile 'com.android.support:design:23.1.1' 
} 

Si noterà alcune dipendenze 'testCompile' su finto potere e si riferiscono d librerie (oltre a AndroidTestCompile). Questo perché per la cintura e le parentesi graffe volevo vedere se potevo ottenere un test di junit (non strumentazione) (che usa power mock) funzionante anche. Sono stato in grado di ottenere questo risultato, ma non il test della strumentazione. Qui è il mio codice (orribile):

attività principale:

package com.example.android.powermocktest; 

import android.os.Bundle; 
import android.support.design.widget.FloatingActionButton; 
import android.support.design.widget.Snackbar; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.Toolbar; 
import android.view.View; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.widget.TextView; 

public class MainActivity extends AppCompatActivity { 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 
    fab.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 
        .setAction("Action", null).show(); 
     } 
    }); 

    TextView textView1 = (TextView)findViewById(R.id.textView1); 
    textView1.setText(GetStaticValue.getTheStaticValue()); 

    TextView textView2 = (TextView)findViewById(R.id.textView2); 
    textView2.setText(new GetNonStaticValue().getNonStaticString()); 
} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.menu_main, menu); 
    return true; 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    int id = item.getItemId(); 

    //noinspection SimplifiableIfStatement 
    if (id == R.id.action_settings) { 
     return true; 
    } 

    return super.onOptionsItemSelected(item); 
} 
} 

file di layout:

<?xml version="1.0" encoding="utf-8"?> 
<android.support.design.widget.CoordinatorLayout   xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:fitsSystemWindows="true" 
tools:context="com.example.android.powermocktest.MainActivity"> 

<android.support.design.widget.AppBarLayout 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:theme="@style/AppTheme.AppBarOverlay"> 

    <android.support.v7.widget.Toolbar 
     android:id="@+id/toolbar" 
     android:layout_width="match_parent" 
     android:layout_height="?attr/actionBarSize" 
     android:background="?attr/colorPrimary" 
     app:popupTheme="@style/AppTheme.PopupOverlay" /> 

</android.support.design.widget.AppBarLayout> 

<include layout="@layout/content_main" /> 

<android.support.design.widget.FloatingActionButton 
    android:id="@+id/fab" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="bottom|end" 
    android:layout_margin="@dimen/fab_margin" 
    android:src="@android:drawable/ic_dialog_email" /> 

</android.support.design.widget.CoordinatorLayout> 

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:paddingBottom="@dimen/activity_vertical_margin" 
android:paddingLeft="@dimen/activity_horizontal_margin" 
android:paddingRight="@dimen/activity_horizontal_margin" 
android:paddingTop="@dimen/activity_vertical_margin" 
app:layout_behavior="@string/appbar_scrolling_view_behavior" 
tools:context="com.example.android.powermocktest.MainActivity" 
tools:showIn="@layout/activity_main"> 

<TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:id="@+id/textView1" 
    android:text="Hello World!" /> 
<TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_below="@+id/textView1" 
    android:id="@+id/textView2" 
    android:text="Hello World again!" /> 
</RelativeLayout> 

e le classi per ottenere i valori statici e non statici:

package com.example.android.powermocktest; 

public class GetStaticValue { 

private final static String THE_VALUE = "Hi, I'm static"; 

public static String getTheStaticValue(){ 
    return THE_VALUE; 
} 
} 

package com.example.android.powermocktest; 

public class GetNonStaticValue { 

public String getNonStaticString(){ 
    return "Hi, I'm non-static"; 
} 
} 

E infine le classi di test. I primi test di strumentazione:

package com.example.android.powermocktest; 

import android.support.test.runner.AndroidJUnit4; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.powermock.api.mockito.PowerMockito; 
import org.powermock.core.classloader.annotations.PrepareForTest; 

import static org.hamcrest.CoreMatchers.equalTo; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.powermock.api.mockito.PowerMockito.when; 

@RunWith(AndroidJUnit4.class) 
@PrepareForTest(GetStaticValue.class) 
public class GetStaticValueTest { 

    @Test 
    public void getStaticValueReturnsValue(){ 
     PowerMockito.mockStatic(GetStaticValue.class); 
     when(GetStaticValue.getTheStaticValue()).thenReturn("Hi, I'm static"); 
     assertThat(GetStaticValue.getTheStaticValue(), equalTo("Hi, I'm static")); 
    } 
} 

package com.example.android.powermocktest; 

import android.support.test.runner.AndroidJUnit4; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Mockito; 

import static org.hamcrest.CoreMatchers.equalTo; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.mockito.Mockito.when; 

@RunWith(AndroidJUnit4.class) 
public class GetNonStaticValueTest { 

    @Test 
    public void getNonStaticValueReturnsNonStaticValue(){ 
     GetNonStaticValue getNonStaticValue = Mockito.mock(GetNonStaticValue.class); 
     when(getNonStaticValue.getNonStaticString()).thenReturn("Hi, I'm non-static"); 
     assertThat(getNonStaticValue.getNonStaticString(), equalTo("Hi, I'm non-static")); 
    } 
} 

e ora il test JUnit (che di lavoro):

package com.example.android.powermocktest; 

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

import static org.hamcrest.CoreMatchers.equalTo; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.powermock.api.mockito.PowerMockito.when; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest(GetStaticValue.class) 
public class GetStaticValueTest { 

    @Test 
    public void getStaticValueReturnsValue() { 
     PowerMockito.mockStatic(GetStaticValue.class); 
     when(GetStaticValue.getTheStaticValue()).thenReturn("Hi, I'm static"); 
     assertThat(GetStaticValue.getTheStaticValue(), equalTo("Hi, I'm static")); 
    } 
} 

quello che ho provato finora:

Ho fatto qualche lettura a questo problema e una soluzione che ho trovato, che non ho provato è quella di aprire manualmente i contenitori della libreria power mock e rimuovere la classe duplicata offendente. Come (al lavoro) usiamo gradle e non lavoriamo con i jar locali preferirei non farlo.

How to get Powermock to work with Dexmaker

persone che ho sentito suggeriscono usando ‘escludere’ all'interno della sezione Android del file di build Gradle. Questo non è adatto in quanto il test di strumentazione per eseguirlo è costruito (da Android Studio/Gradle) come apk e lanciato sul dispositivo. Di conseguenza sono necessari i power mock jars per eseguire i test.

android studio: gradle dependency error

Ho provato a rimuovere alcune delle dipendenze che vedete elencati nel mio file di generazione sopra. Ad esempio, ho provato a rimuovere la dipendenza "org.powermock: powermock-api-mockito", ma è necessario usare "mockStatic" per esempio.

sembra che qualcuno ha avuto successo quando si utilizza Maven, dicendo Maven per escludere i duplicati:

https://groups.google.com/forum/#!searchin/powermock/android/powermock/ndZ2ZliYGCY/Eh226605u2cJ

PowerMock + Mockito + Maven on Android app showing Dex loader error

ho cercato di vedere se c'è un modo di raccontare a Gradle ignora le classi duplicate all'interno dei barattoli delle dipendenze, ma finora non ho avuto successo.

Credo che valga la pena perseguire come prima cosa, il power mock può essere molto utile se usato con parsimonia e in secondo luogo, non posso credere che questo problema non sia stato risolto (è stato sicuramente riscontrato prima!).

Come ultimo punto, ho notato che Google se stessi sembrano suggerire che intrinsecamente beffardo è solo per unità, non i test di strumentazione:

http://developer.android.com/training/testing/start/index.html

Tutto l'aiuto che chiunque può offrire sarebbe molto apprezzato. Grazie.

+0

Sei stato capace di risolvere questo problema? – thedarkpassenger

+0

No, non ancora, questo problema è ancora in sospeso. – Kai

risposta

0

Avete questo nel vostro progetto/gradle del modulo?

packagingOptions { 
    exclude 'fileNameYouWantToExclude' 
} 

In questo modo Androd metterà un solo file, se i duplicati sono trovati

+0

Il mio problema sono classi duplicate in diversi barattoli. Ho bisogno di altre classi all'interno di questi vasi, quindi rimuovere (o escludere) un intero barattolo non è possibile. Attualmente sembra che la soluzione sarebbe un sacco di hacking jar che non è una soluzione praticabile. A meno che qualcuno non conosca un modo per dire al grado di ignorare le classi duplicate? – Kai

2

ero in grado di risolvere questo problema con il seguente. Se vedi un messaggio di errore che dice che i file duplicati sono stati copiati in APK [nome file], aggiungi quel [nome file] da escludere in packagingOptions.

android { 
    packagingOptions { 
     exclude 'mockito-extensions/org.mockito.plugins.MockMaker' 
    } 
} 
+0

Ho usato sopra l'esclusione e ora non ho trovato nessun test. –

+0

Ciò causa un errore durante il caricamento dei miei test, non funziona per me – Miquel