Próbuję użyć PowerMocka do wyśmiewania klasy metodą statyczną, ale w szczególności chcę to zrobić w ciągu Test oprzyrządowania Android. Aby było jasne, chcę przeprowadzić test na prawdziwym urządzeniu z Androidem lub emulatorze. Korzystam z Android Studio (1.5.1) i Gradle (1.5.0). Aby uniknąć czerwonych śledzi, stworzyłem naprawdę prostą i raczej prymitywną aplikację "cześć świat". Ta aplikacja pokazuje po prostu 2 fragmenty tekstu, jeden pobrany ze statycznej metody i jeden z niestatycznej metody. Napisałem testy instrumentacyjne dla obu tych klas "dostawców tekstu". Można zobaczyć tę aplikację tutaj:Używanie PowerMock i Mockito w teście Instrumentacji Android - Błąd - Powielanie plików - org.mockito.plugins.MockMaker
https://github.com/Kai2k/PowerMockAndroidTest.git
Kiedy próbuje uruchomić testy oprzyrządowania pojawia się błąd, który to wydaje wielu ludzi stara się osiągnąć ten otrzymujemy:
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
Moje współzależności na mojej aplikacji build.gradle plik wyglądać następująco:
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'
}
można zauważyć pewne zależności „testCompile” na mock zasilania i dotyczą d bibliotek (oprócz androidTestCompile). Wynika to z tego, że w przypadku pasa i szelek chciałbym sprawdzić, czy uda mi się również wykonać test łączący (bez narzędzi) (który wykorzystuje siłę próbną). Udało mi się to osiągnąć, ale nie test oprzyrządowania. Oto moje (straszne) Kod:
Główna działalność:
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);
}
}
plików układu:
<?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>
I klasy, aby uzyskać statyczne i nie statyczne wartości:
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";
}
}
I wreszcie klasy testowe. Pierwsze testy oprzyrządowania:
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"));
}
}
a teraz testy JUnit (pracujące):
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"));
}
}
Co próbowałem dotąd:
Zrobiłem trochę czytania tego problemu i jedno rozwiązanie, które znalazłem, czego nie próbowałem, to ręcznie złamać słoiki biblioteki i udowodnić, że są szkodliwe duplikaty. Jako (w pracy) używamy gradle i nie pracujemy z lokalnymi słoikami, których wolałbym nie robić.
How to get Powermock to work with Dexmaker
Słyszałem, że ludzie sugerują, używając „wyklucza” w Android części Gradle pliku kompilacji. To nie jest odpowiednie, ponieważ do testu oprzyrządowania jest ono uruchamiane (przez Android Studio/Gradle) jako apk i uruchamiane na urządzeniu. Jako takie, słoiki z próbnymi mocami są potrzebne do przeprowadzenia testów.
android studio: gradle dependency error
Próbowałem usuwając niektóre z zależnościami, które widzisz wymienionych w moim pliku kompilacji powyżej. Próbowałem na przykład usunąć zależność "org.powermock: powermock-api-mockito", ale na przykład trzeba użyć "mockStatic".
Wydaje się, że ktoś był udany przy użyciu Maven, mówiąc Maven wykluczyć duplikaty:
https://groups.google.com/forum/#!searchin/powermock/android/powermock/ndZ2ZliYGCY/Eh226605u2cJ
PowerMock + Mockito + Maven on Android app showing Dex loader error
Próbowałem sprawdzić, czy istnieje sposób, aby opowiadać Gradle zignoruj zduplikowane klasy w słoikach zależności, ale jak dotąd nie udało mi się.
Uważam, że warto poszukać, po pierwsze, moc makiety może być bardzo przydatna, gdy używana oszczędnie, a po drugie, nie mogę uwierzyć, że ta kwestia nie została rozwiązana (na pewno napotkano ją wcześniej!).
Jako ostatni punkt, ja zauważyłem, że Google sami zdają się sugerować, że z natury szyderczy jest tylko dla jednostki, a nie testów oprzyrządowania:
http://developer.android.com/training/testing/start/index.html
Każda pomoc ktoś może zaoferować byłoby bardzo mile widziane. Dzięki.
Czy byłeś w stanie rozwiązać ten problem? – thedarkpassenger
Nie, jeszcze nie, ten problem jest wciąż nierozstrzygnięty. – Kai