在Android Instrumentation测试中使用PowerMock和Mockito

编程入门 行业动态 更新时间:2024-10-09 14:25:40
本文介绍了在Android Instrumentation测试中使用PowerMock和Mockito - 错误 - 重复文件 - org.mockito.plugins.MockMaker的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我尝试使用PowerMock来模拟一个静态方法的类,但我希望在Android Instrumentation测试中做到这一点。为了清楚,我希望在真实的Android设备或模拟器上运行测试。 我正在使用Android Studio(1.5.1)和Gradle(1.5.0)。为了避免任何红鲱鱼,我已经创建了一个非常基本的,相当粗糙的'你好世界'的应用程序。这个应用程序只显示2个文本,一个从静态方法检索,一个从非静态方法检索。我已经为这两个'文本提供者'类写了测试测试。你可以在这里看到这个应用程序:

https: //github/Kai2k/PowerMockAndroidTest.git

当试图运行仪器测试时,我看到很多人试图达到这个目的的错误get:

com.android.build.api.transform.TransformException:com.android.builder.packaging.DuplicateFileException:重复文件在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

我对应用的build.gradle文件的依赖关系看起来像这:

依赖关系{编译fileTree(包括:''.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'c​​om.android.support:support-annotations:23.1.1' androidTestCompile'c​​om.android.support.test:runner:0.4.1' androidTestCompile'c​​om.android.support.test:rules:0.4.1' androidTestCompile'org.hamcrest:hamcrest -library:1.3' androidTestCompile'c​​om.crittercism.dexmaker:dexmaker:1.4' androidTestCompile'c​​om.crittercism.dexmaker:dexmaker-mockito:1.4' compile'c​​o m.android.support:appcompat-v7:23.1.1' compile'c​​om.android.support:design:23.1.1'}

你会发现一些'testCompile'依赖于power mock和相关库(除了androidTestCompile)。这是因为对于腰带和大括号,我想看看是否可以使用junit(非仪器)测试(它使用功率模拟)也起作用。我能够实现这一点,但不能进行仪器测试。这是我的(可怕的)代码:

主要活动:

package com.example.android.powermocktest; 导入android.os.Bundle; 导入android.support.design.widget.FloatingActionButton; 导入android.support.design.widget.Snackbar; 导入android.support.v7.app.AppCompatActivity; 导入android.support.v7.widget.Toolbar; 导入android.view.View; 导入android.view.Menu; 导入android.view.MenuItem; 导入android.widget.TextView; $ b $ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); 工具栏工具栏=(工具栏)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(菜单菜单){ //膨胀菜单;这会将项目添加到操作栏(如果存在)。 getMenuInflater()。inflate(R.menu.menu_main,menu); 返回true; } @Override public boolean onOptionsItemSelected(MenuItem item){ // Handle action bar item clicks here here。操作栏将 //自动处理Home / Up按钮上的点击,只要您在AndroidManifest.xml中指定了一个父活动,就可以长按 //。 int id = item.getItemId(); // noinspection SimplifiableIfStatement if(id == R.id.action_settings){ return true; } 返回super.onOptionsItemSelected(item);

布局文件:

<?xml version =1.0encoding =utf-8?> < android.support.design.widget.CoordinatorLayout xmlns:android =schemas.android/apk/res/android xmlns:app =http:// schemas .android / apk / res-auto xmlns:tools =schemas.android/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 = @风格/ 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/ > 以及获得静态和非静态值的类:

package com.example.android.powermocktest; public class GetStaticValue { private final static String THE_VALUE =我是静态的; public static String getTheStaticValue(){ return THE_VALUE; } } package com.example.android.powermocktest; public class GetNonStaticValue { public String getNonStaticString(){ return我是非静态的; } }

最后是测试类。首先是测试测试:

package com.example.android.powermocktest; 导入android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; 导入org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; 导入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); 当(GetStaticValue.getTheStaticValue())。然后返回(我是静态的); assertThat(GetStaticValue.getTheStaticValue(),equalTo(Hi,I'm static)); } } package com.example.android.powermocktest; 导入android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; 导入static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class GetNonStaticValueTest { $ b @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)); $ b $ p $ b现在jUnit测试(工作):

package com.example.android.powermocktest; import org.junit.Test; import org.junit.runner.RunWith; 导入org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; 导入org.powermock.modules.junit4.PowerMockRunner; 导入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); 当(GetStaticValue.getTheStaticValue())。然后返回(我是静态的); assertThat(GetStaticValue.getTheStaticValue(),equalTo(Hi,I'm static));

到目前为止我已经尝试过:

我已经对这个问题做了一些解读,我找到的一个解决方案我没有尝试过,那就是手动打开power mock库jar并删除违规的重复类。作为(在工作中),我们使用gradle并且不使用本地jar,我不希望这样做。

如何让Powermock与Dexmaker合作

我听说有人建议在gradle构建文件的Android部分使用'exclude'。这是不适合的,因为运行它的仪器测试是通过Android(Android Studio / Gradle)构建的,并且在设备上启动。因此需要动力模拟罐子来运行测试。

android studio:gradle依赖关系错误

我尝试删除您在上面的构建文件中列出的一些依赖关系。例如,我尝试删除'org.powermock:powermock-api-mockito'依赖项,但需要使用'mockStatic'。

看来通过告知Maven排除重复内容,某人在使用Maven时取得了成功: groups.google/forum/#!searchin/powermock/android/powermock/ndZ2ZliYGCY/Eh226605u2cJ

PowerMock + Mockito + Maven在Android应用上显示Dex加载器错误

我试着看看是否有方法告诉Gradle忽略依赖关系jar中的重复类,但到目前为止我还没有成功。

我觉得这是值得追求的,首先,功能模拟在使用sp时非常有用第二,我不能相信这个问题还没有解决(这是以前遇到的!)。

作为最后一点,我确实注意到Google自己似乎本质上建议嘲笑仅适用于单位,而不是仪器测试: index.htmlrel =nofollow noreferrer> developer.android/training/testing/start/index.html

任何帮助任何人可以提供将不胜感激。感谢。

解决方案

我能够用以下方法解决此问题。如果您看到任何说明复制文件在APK [文件名]中复制的错误消息,请将[filename]添加到packagingOptions中以排除。

I"m trying to use PowerMock to mock a class with a static method, but specifically I wish to do this within an Android Instrumentation test. To be clear I wish to run the test on a real Android device or an emulator. I am using Android Studio (1.5.1) and Gradle (1.5.0). To avoid any red herrings I have created a really basic and rather crude ‘hello world’ app. This app simply shows 2 pieces of text, one retrieved from a static method and one from a non-static method. I have written instrumentation tests for both of these ‘text provider’ classes. You can see this app here:

github/Kai2k/PowerMockAndroidTest.git

When attempting to run the instrumentation tests I get the error which it appears many people trying to achieve this 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

My dependencies on my app’s build.gradle file look like this:

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

You will notice some ‘testCompile’ dependencies on power mock and related libraries (in addition to androidTestCompile). This is because for belt and braces I wanted to see if I could get a junit (non-instrumentation) test (which uses power mock) working also. I was able to achieve this, but not the instrumentation test. Here is my (horrible) code:

Main Activity:

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); } }

Layout files:

<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="schemas.android/apk/res/android" xmlns:app="schemas.android/apk/res-auto" xmlns:tools="schemas.android/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="schemas.android/apk/res/android" xmlns:app="schemas.android/apk/res-auto" xmlns:tools="schemas.android/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>

And the classes to get the static and non-static values:

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"; } }

And finally the test classes. First the instrumentation tests:

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")); } }

And now the jUnit tests (which work):

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")); } }

What I’ve tried so far:

I’ve done some reading around this issue and one solution I’ve found, which I have not tried is to manually crack open the power mock library jars and remove the offending duplicate class. As (at work) we use gradle and don’t work with local jars I’d prefer not to do this.

How to get Powermock to work with Dexmaker

I’ve heard people suggest using ‘exclude’ within the Android section of the gradle build file. This is not suitable as for the instrumentation test to run it is built (by Android Studio / Gradle) as an apk and launched on the device. As such the power mock jars are needed to run the tests.

android studio: gradle dependency error

I’ve tried removing some of the dependencies which you see listed in my build file above. For example I’ve tried removing the ‘org.powermock:powermock-api-mockito’ dependency, but it is needed to use ‘mockStatic’ for example.

It seems that someone was successful when using Maven, by telling Maven to exclude duplicates:

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

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

I’ve tried to see if there is a way of telling Gradle to ignore duplicate classes within dependency jars but so far I have been unsuccessful.

I feel this is worth pursuing as firstly, power mock can be very useful when used sparingly and second, I cannot believe that this issues has not been solved (it’s certainly been encountered before!).

As a final point, I did notice that Google themselves seem to inherently suggest that mocking is only for unit, not instrumentation tests:

developer.android/training/testing/start/index.html

Any help anyone can offer would be greatly appreciated. Thanks.

解决方案

I was able to workaround this issue with the following. If you see any error message that says Duplicate files copied in APK [filename], add that [filename] to be excluded in the packagingOptions.

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

更多推荐

在Android Instrumentation测试中使用PowerMock和Mockito

本文发布于:2023-10-31 06:49:05,感谢您对本站的认可!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:测试中   Instrumentation   Android   PowerMock   Mockito

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!