如何模拟最终 class?
How make mock of final class?
在 AndroidStudio 工作。想要使用 PowerMock 模拟 AndroidInstrumentalTest 的最终 class。在 gradle 中添加了库:
androidTestCompile ('org.powermock:powermock-api-mockito:1.5.6')
androidTestCompile ('org.powermock:powermock-core:1.5.6')
androidTestCompile ('org.powermock:powermock-module-junit4:1.5.6')
androidTestCompile 'org.mockito:mockito-core:1.10.8'
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
进行测试class:
public class BluetoothTest extends ActivityInstrumentationTestCaseWithLogIn<PlanActivity> {
public BluetoothTest() {
super(PlanActivity.class);
}
@Override
public void setUp() throws Exception{
super.setUp();
BluetoothGatt gatt = PowerMockito.mock(BluetoothGatt.class);
}
}
堆栈跟踪:
java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
........
at android.support.test.internal.runner.junit3.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
........
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1732)
我检查了来自 this issue, with no luck. Apparently solutions help with mockito-core, not powermock. It proved by another issue
的解决方案
我看到了 docs,然后尝试在测试 class 上添加注释 @RunWith(PowerMockRunner.class) 和 @PrepareForTest(BluetoothGatt.class)。结果:
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
at org.powermock.modules.junit4.common.internal.impl.JUnitVersion.getJUnitVersion(JUnitVersion.java:28)
at org.powermock.modules.junit4.common.internal.impl.JUnitVersion.isGreaterThanOrEqualTo(JUnitVersion.java:23)
at org.powermock.modules.junit4.PowerMockRunner.getRunnerDelegateImplClass(PowerMockRunner.java:38)
at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:33)
........
at org.junit.runner.Computer.getSuite(Computer.java:26)
at android.support.test.internal.runner.TestRequestBuilder.classes(TestRequestBuilder.java:598)
at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:578)
at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:542)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:269)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1732)
在非 root 设备上,APK 受 "verification" 约束,其中 VM 确保 classes 仅引用它们编译所针对的实现。模拟器和 root 设备不受此规则约束。
不幸的是,破解 classloader(PowerMock 和 Robolectric)的实现几乎总是违反该规则,因为它们创建了意想不到的静态 class 和方法实现(为了模拟它们).
让 classloader 攻击您的 JVM 单元测试,而不是您的仪器测试,而是将任何相关接口提取到您可以控制(和模拟)的包装器中。
您是否尝试过使用 Robolectric
+ Mockito
?
我相信你的想法是正确的。它应该很简单:
BluetoothGatt gatt = mock(BluetoothGatt.class);
请看这个例子:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
private BluetoothGattCharacteristic characteristic;
private OnBoardingService service;
private BluetoothGattReceiver receiver = new BluetoothGattReceiver();
@Before
public void initialise() {
BleDevice bleDevice = mock(BleDevice.class);
when(bleDevice.getType()).thenReturn(BleDeviceType.WunderbarMIC);
BluetoothGatt gatt = mock(BluetoothGatt.class);
BluetoothGattService gattService = mock(BluetoothGattService.class);
when(gatt.getServices()).thenReturn(Arrays.asList(gattService));
when(gattService.getUuid()).thenReturn(fromString("00002001-0000-1000-8000-00805f9b34fb"));
characteristic = mock(BluetoothGattCharacteristic.class);
when(gattService.getCharacteristics()).thenReturn(Arrays.asList(characteristic));
service = new OnBoardingService(bleDevice, gatt, receiver);
}
来源:https://github.com/relayr/android-sdk/blob/master/tests/src/androidTest/java/io/relayr/ble/service/OnBoardingServiceTest.java
https://github.com/relayr/android-sdk/blob/master/tests/src/androidTest/java/io/relayr/ble/service/BluetoothGattReceiverTest.java
在 AndroidStudio 工作。想要使用 PowerMock 模拟 AndroidInstrumentalTest 的最终 class。在 gradle 中添加了库:
androidTestCompile ('org.powermock:powermock-api-mockito:1.5.6')
androidTestCompile ('org.powermock:powermock-core:1.5.6')
androidTestCompile ('org.powermock:powermock-module-junit4:1.5.6')
androidTestCompile 'org.mockito:mockito-core:1.10.8'
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
进行测试class:
public class BluetoothTest extends ActivityInstrumentationTestCaseWithLogIn<PlanActivity> {
public BluetoothTest() {
super(PlanActivity.class);
}
@Override
public void setUp() throws Exception{
super.setUp();
BluetoothGatt gatt = PowerMockito.mock(BluetoothGatt.class);
}
}
堆栈跟踪:
java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
........
at android.support.test.internal.runner.junit3.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
........
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1732)
我检查了来自 this issue, with no luck. Apparently solutions help with mockito-core, not powermock. It proved by another issue
的解决方案我看到了 docs,然后尝试在测试 class 上添加注释 @RunWith(PowerMockRunner.class) 和 @PrepareForTest(BluetoothGatt.class)。结果:
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
at org.powermock.modules.junit4.common.internal.impl.JUnitVersion.getJUnitVersion(JUnitVersion.java:28)
at org.powermock.modules.junit4.common.internal.impl.JUnitVersion.isGreaterThanOrEqualTo(JUnitVersion.java:23)
at org.powermock.modules.junit4.PowerMockRunner.getRunnerDelegateImplClass(PowerMockRunner.java:38)
at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:33)
........
at org.junit.runner.Computer.getSuite(Computer.java:26)
at android.support.test.internal.runner.TestRequestBuilder.classes(TestRequestBuilder.java:598)
at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:578)
at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:542)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:269)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1732)
在非 root 设备上,APK 受 "verification" 约束,其中 VM 确保 classes 仅引用它们编译所针对的实现。模拟器和 root 设备不受此规则约束。
不幸的是,破解 classloader(PowerMock 和 Robolectric)的实现几乎总是违反该规则,因为它们创建了意想不到的静态 class 和方法实现(为了模拟它们).
让 classloader 攻击您的 JVM 单元测试,而不是您的仪器测试,而是将任何相关接口提取到您可以控制(和模拟)的包装器中。
您是否尝试过使用 Robolectric
+ Mockito
?
我相信你的想法是正确的。它应该很简单:
BluetoothGatt gatt = mock(BluetoothGatt.class);
请看这个例子:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
private BluetoothGattCharacteristic characteristic;
private OnBoardingService service;
private BluetoothGattReceiver receiver = new BluetoothGattReceiver();
@Before
public void initialise() {
BleDevice bleDevice = mock(BleDevice.class);
when(bleDevice.getType()).thenReturn(BleDeviceType.WunderbarMIC);
BluetoothGatt gatt = mock(BluetoothGatt.class);
BluetoothGattService gattService = mock(BluetoothGattService.class);
when(gatt.getServices()).thenReturn(Arrays.asList(gattService));
when(gattService.getUuid()).thenReturn(fromString("00002001-0000-1000-8000-00805f9b34fb"));
characteristic = mock(BluetoothGattCharacteristic.class);
when(gattService.getCharacteristics()).thenReturn(Arrays.asList(characteristic));
service = new OnBoardingService(bleDevice, gatt, receiver);
}
来源:https://github.com/relayr/android-sdk/blob/master/tests/src/androidTest/java/io/relayr/ble/service/OnBoardingServiceTest.java https://github.com/relayr/android-sdk/blob/master/tests/src/androidTest/java/io/relayr/ble/service/BluetoothGattReceiverTest.java