如何使用Mock修改Android設備上的features
背景
手工測試過程中有個測試場景需要刪除測試設備上某個Android feature,往上搜索瞭一圈沒找到有效的操作方法。獲取Android設備所有的feature可以通過adb命令pm list features或者Android代碼Context.getPackageManager().getSystemAvailableFeatures(),但都沒有對應的修改方法。
既然feature是從Context獲取的,那能不能構造一個隻包含我想要的feature的Context呢。順著這個思路,有瞭下面的方案。
方案設計
單元測試常用的Mock技術,就是來構造假/模擬對象的。但如果完全構造,又擔心和真實環境差別較大,測試結果不可靠。能不能從真實Android設備中獲取真實的Context,把不想要的feature去除,再給到被測試方法中呢?
答案是肯定的,通過選用流行的Mock組件Mockito ,官網上給出瞭下面2種Mock Java對象的方式:
mock()
/@Mock
: create mockspy()
/@Spy
: partial mocking, real methods are invoked but still can be verified and stubbed
可以看到Spy這種Mock方式可完美地解決我的需求。
實現(簡化版需求)
業務需求說明
獲取所有Android Features並把feature name打印在日志中,使用adb命令的效果如下:
➜ study git:(master) ✗ adb shell pm list features feature:reqGlEsVersion=0x30002 feature:android.hardware.audio.output feature:android.hardware.bluetooth feature:android.hardware.bluetooth_le feature:android.hardware.camera feature:android.hardware.camera.any feature:android.hardware.camera.autofocus feature:android.hardware.camera.capability.manual_post_processing feature:android.hardware.camera.capability.manual_sensor feature:android.hardware.camera.capability.raw feature:android.hardware.camera.flash feature:android.hardware.camera.front feature:android.hardware.camera.level.full feature:android.hardware.faketouch feature:android.hardware.location feature:android.hardware.location.gps feature:android.hardware.location.network feature:android.hardware.microphone feature:android.hardware.nfc.any feature:android.hardware.opengles.aep feature:android.hardware.ram.normal feature:android.hardware.screen.landscape feature:android.hardware.screen.portrait feature:android.hardware.sensor.accelerometer feature:android.hardware.sensor.compass feature:android.hardware.sensor.light feature:android.hardware.sensor.proximity feature:android.hardware.sensor.stepcounter feature:android.hardware.telephony feature:android.hardware.telephony.cdma feature:android.hardware.telephony.gsm feature:android.hardware.touchscreen feature:android.hardware.touchscreen.multitouch feature:android.hardware.touchscreen.multitouch.distinct feature:android.hardware.touchscreen.multitouch.jazzhand feature:android.hardware.usb.accessory feature:android.hardware.usb.host feature:android.hardware.vulkan.compute feature:android.hardware.vulkan.level feature:android.hardware.vulkan.version=4194307 feature:android.hardware.wifi feature:android.hardware.wifi.direct feature:android.software.activities_on_secondary_displays feature:android.software.app_widgets feature:android.software.autofill feature:android.software.backup feature:android.software.companion_device_setup feature:android.software.connectionservice feature:android.software.cts feature:android.software.device_admin feature:android.software.file_based_encryption feature:android.software.input_methods feature:android.software.live_wallpaper feature:android.software.managed_users feature:android.software.midi feature:android.software.picture_in_picture feature:android.software.print feature:android.software.securely_removes_users feature:android.software.sip feature:android.software.sip.voip feature:android.software.verified_boot feature:android.software.voice_recognizers feature:android.software.webview
業務需求代碼實現
public class FeaturesUtil { private static final String TAG = "FeaturesUtil"; public static void getFeatures(Context context) { PackageManager packageManager = context.getPackageManager(); FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures(); for (FeatureInfo featureInfo : featureInfos) { Log.i(TAG, "feature: " + featureInfo.name); } } }
測試代碼實現(去除bluetooth feature)
@Spy Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();; @Spy PackageManager packageManager = context.getPackageManager(); @Test public void getFeatures() { // 去除bluetooth feature FeatureInfo[] mockedFeatureInfos = removeFeature(packageManager.getSystemAvailableFeatures(), Arrays.asList("android.hardware.bluetooth", "android.hardware.bluetooth_le")); when(packageManager.getSystemAvailableFeatures()).thenReturn(mockedFeatureInfos); when(context.getPackageManager()).thenReturn(packageManager); FeaturesUtil.getFeatures(context); } /** * 根據feature name刪除一個或多個feature * * @param featureInfos FeatureInfo數組 * @param featureNames 字符串列表,每個元素是要刪除的FeatureInfo的name * @return 刪除之後的FeatureInfo數組 */ private FeatureInfo[] removeFeature(FeatureInfo[] featureInfos, List<String> featureNames) { List<FeatureInfo> featureInfoList = new ArrayList(); for (FeatureInfo featureInfo : featureInfos) { if (featureInfo.name != null && !(featureNames.contains(featureInfo.name))) { featureInfoList.add(featureInfo); } } return featureInfoList.toArray(new FeatureInfo[featureInfoList.size()]); }
運行測試代碼後Logcat輸出
可以看到bluetooth相關的feature已經沒有瞭
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.sensor.proximity
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.sensor.accelerometer
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.faketouch
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.usb.accessory
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.telephony.cdma
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.backup
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.touchscreen
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.touchscreen.multitouch
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.print
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.activities_on_secondary_displays
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.voice_recognizers
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.picture_in_picture
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.opengles.aep
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.autofocus
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.telephony.gsm
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.sip.voip
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.usb.host
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.audio.output
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.verified_boot
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.flash
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.front
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.screen.portrait
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.microphone
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.autofill
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.securely_removes_users
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.sensor.compass
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.hardware.touchscreen.multitouch.jazzhand
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.app_widgets
2021-04-07 13:23:39.266 16238-16268/? I/FeaturesUtil: feature: android.software.input_methods
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.sensor.light
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.vulkan.version
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.companion_device_setup
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.device_admin
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.screen.landscape
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.ram.normal
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.managed_users
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.webview
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.sensor.stepcounter
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.capability.manual_post_processing
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.any
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.capability.raw
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.vulkan.compute
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.connectionservice
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.touchscreen.multitouch.distinct
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.location.network
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.cts
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.sip
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.capability.manual_sensor
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.camera.level.full
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.wifi.direct
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.live_wallpaper
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.location.gps
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.software.midi
2021-04-07 13:23:39.267 16238-16268/? I/FeaturesUtil: feature: android.hardware.nfc.any
2021-04-07 13:23:39.268 16238-16268/? I/FeaturesUtil: feature: android.hardware.wifi
2021-04-07 13:23:39.268 16238-16268/? I/FeaturesUtil: feature: android.hardware.location
2021-04-07 13:23:39.268 16238-16268/? I/FeaturesUtil: feature: android.hardware.vulkan.level
2021-04-07 13:23:39.268 16238-16268/? I/FeaturesUtil: feature: android.hardware.telephony
2021-04-07 13:23:39.268 16238-16268/? I/FeaturesUtil: feature: android.software.file_based_encryption
2021-04-07 13:23:39.269 16238-16268/? I/TestRunner: finished: getFeatures(com.aniu.featuresmock.FeaturesUtilTest)
總結
- 不直接使用Mock,而是使用Spy,最大限度保持測試環境真實可靠,從而保證測試效果
- 測試代碼不要放在test目錄,要放在androidTest目錄,保證在真實設備上跑(不然代碼也會報錯)。目錄結構如下:
以上就是如何使用Mock修改Android設備上的features的詳細內容,更多關於Mock修改Android features的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- None Found