Appium 模拟
您可能需要测试使用相机拍摄照片或扫描条形码的应用程序,或者使用身份验证来保护登录或付款等敏感功能的应用程序。如果要测试的设备在物理上不可用,则会出现问题。使用模拟,您可以测试应用程序的这些方面,而无需访问物理设备。
注: 仅注入代码的应用程序支持模拟。要安装并启动注入代码的应用程序,请将 installPackageApp 功能设置为 true。
生物识别
您可以在 Appium 测试中模拟指纹和人脸识别 (仅 iOS) 身份验证。使用以下参数和值指定要用于仿真的数据。
模拟数据
参数 | 值 |
---|---|
authResult |
模拟结果:
|
authType
|
身份验证类型: 指纹 备注: 指纹 authType 也用于 FaceID。 |
authResultDetails |
模拟失败或取消时的结果原因。当 authResult = Success 时,将该值保留为空。 authResult = 失败
|
authResult = 取消
|
操作
指定测试期间要执行的仿真类型:
参数 | 值 |
---|---|
操作 |
身份验证 |
照片、条形码和二维码模拟
您可以模拟拍摄照片,或扫描 Appium 测试中的条形码或二维码。使用摄影机模拟时,可以指定要使用的图像,而不是摄影机的实际输出。
参数 | 值 |
---|---|
内容类型 | 图像 |
文件名
|
任何文件名,包括后缀。 |
上载介质 | 介质应以 base64 编码。 |
操作 |
正在执行的仿真类型:
|
注: 要关闭摄像机模拟,请将 uploadMedia 设置为“ 0 ”。
代码示例
以下部分提供了将 Digital Lab 摄像机和身份验证模拟用于 Appium 的代码示例。有关使用 Appium 和 Digital Lab 的工作项目,请参阅 Digital Lab github 存储库.
Android 身份验证和相机模拟 - Java
示例代码使用 Java 客户端 v8。对于 Java 客户端 v7.x,请参阅 Digital Lab github repository 中的示例.
import com.google.common.io.Resources;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.touch.offset.PointOption;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebElement;
import java.awt.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Base64;
import java.util.HashMap;
import java.util.Objects;
public class Simulation {
private static AndroidDriver driver;
private static String SERVER = "https://digitallab-server:8443";
public static void main(String[] args) throws MalformedURLException {
UiAutomator2Options caps = new UiAutomator2Options();
caps.setCapability("platformName", "Android");
caps.setCapability("udid", "8BSX1EDTD");
caps.setCapability("automationName", "UiAutomator2");
caps.setCapability("appPackage", "com.Advantage.aShopping");
caps.setCapability("appActivity", "com.Advantage.aShopping.SplashActivity");
caps.setCapability("dl:oauthClientId", "oauth2-tGR1nN6ZxLQNMrKH4Hq9@opentext.com");
caps.setCapability("dl:oauthClientSecret", "A3BNk1Tc4gbf7o2jDU5m");
caps.setCapability("dl:tenantId", "999999999");
//Simulations are supported only on packaged apps. The below capability instructs Digital Lab to install the packaged version of the app. Default value is false
caps.setCapability("dl:installPackagedApp", true);
driver = new AndroidDriver(new URL(SERVER + "/wd/hub"), caps);
System.out.println("Digital Lab session was successfully created [Android device]");
try{
//Implicit wait
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.findElement(AppiumBy.id("imageViewMenu")).click();
driver.findElement(AppiumBy.id("textViewMenuUser")).click();
WebElement username = driver.findElement(AppiumBy.xpath("//*[@resource-id='com.Advantage.aShopping:id/AosEditTextLoginUserName']/android.widget.EditText[1]"));
username.click();
username.sendKeys("Mercury");
WebElement password = driver.findElement(AppiumBy.xpath("//*[@resource-id='com.Advantage.aShopping:id/AosEditTextLoginPassword']/android.widget.EditText[1]"));
password.click();
password.sendKeys("Mercury");
driver.findElement(AppiumBy.id("buttonLogin")).click();
Thread.sleep(3000);
String message = driver.findElement(AppiumBy.id("android:id/message")).getText();
if(message.contains("Would you like to use fingerprint authentication for logging in ?")){
driver.findElement(AppiumBy.id("android:id/button1")).click();
//Perform biometric simulation
System.out.println("Biometric simulation result:" + biometricSimulation("Success",""));
}
driver.findElement(AppiumBy.androidUIAutomator("new UiSelector().textContains(\"LAPTOPS\")")).click();
driver.findElement(AppiumBy.androidUIAutomator("new UiSelector().textContains(\"HP CHROMEBOOK 14 G1(ENERGY STAR)\")")).click();
int requiredSwipes = driver.findElements(AppiumBy.xpath("//*[@resource-id='com.Advantage.aShopping:id/linearLayoutImagesLocation']/android.widget.FrameLayout")).size();
WebElement imageList = driver.findElement(AppiumBy.id("viewPagerImagesOfProducts"));
for(int i = 1;i <= requiredSwipes;i++){
swipeElement(((RemoteWebElement) imageList).getId(),"up", 1);
}
//load the photo before opening the camera
System.out.println("Photo simulation result:" + cameraSimulation("image","cat.png","camera"));
imageList.click();
//The steps below are performed on the device's camera and correspond to a device with Android 12.
//These steps may change depending on the device model/brand since the camera app might be different.
driver.findElement(AppiumBy.accessibilityId("Take photo")).click();
driver.findElement(AppiumBy.accessibilityId("Done")).click();
//Barcode simulation sample usage:
//System.out.println(cameraSimulation("image","QRCode.jpg","barcode"));
}catch (Exception e){
e.printStackTrace();
}finally {
if (driver != null) driver.quit();
}
}
/**
* Biometric authentication simulation enable/update command.
* @param authResult The simulate result: Failure, Success, or Cancel
* @param authResultDetails The simulate reason when Failure/Cancel
* Failure: NotRecognized, Lockout, FingerIncomplete(Android only), SensorDirty(Android only), NoFingerprintRegistered(iOS only)
* Cancel: System, User
* @return The result of ExecuteScript
*/
private static String biometricSimulation(String authResult, String authResultDetails) throws InterruptedException {
HashMap<String, Object> sensorSimulationMap = new HashMap<String, Object>();
HashMap<String, String> simulationData = new HashMap<String, String>();
simulationData.put("authResult", authResult);
simulationData.put("authType", "Fingerprint");
simulationData.put("authResultDetails", authResultDetails);
sensorSimulationMap.put("simulationData", simulationData);
sensorSimulationMap.put("action", "authentication");
sensorSimulationMap.put("action", "authentication");
//Execute the script and convert the result to a JSON string
String simulationResult = new Gson().toJson(driver.executeScript("mc:sensorSimulation", sensorSimulationMap));
//Return just the message value from simulationResult
return new Gson().fromJson(simulationResult, JsonObject.class).get("message").getAsString();
}
/**
*
* @param contentType Image
* @param fileName Any file name
* @param action Action: barcode, camera
* @return
*/
private static String cameraSimulation(String contentType, String fileName, String action) throws IOException, URISyntaxException, InterruptedException {
Path path = Paths.get(Objects.requireNonNull(Resources.getResource("photo/" + fileName)).toURI());
byte[] bytes = Files.readAllBytes(path);
String encodedString = Base64.getEncoder().encodeToString(bytes);
HashMap<String, String> sensorSimulationMap = new HashMap<>();
sensorSimulationMap.put("uploadMedia", encodedString);
sensorSimulationMap.put("contentType", contentType);
sensorSimulationMap.put("fileName", fileName);
sensorSimulationMap.put("action", action);
//Execute the script and convert the result to a JSON string
String simulationResult = new Gson().toJson(driver.executeScript("mc:sensorSimulation", sensorSimulationMap));
//Return just the message value from simulationResult
return new Gson().fromJson(simulationResult, JsonObject.class).get("message").getAsString();
}
/**
*
* @param elementId The id of the element to be swiped
* @param direction Swipe direction. Mandatory value. Acceptable values are: up, down, left and right (case insensitive)
* @param percent The size of the swipe as a percentage of the swipe area size. Valid values must be float numbers in range 0..1, where 1.0 is 100%
*/
private static void swipeElement(String elementId, String direction, double percent){
HashMap<String, Object> swipeMap = new HashMap<>();
swipeMap.put("elementId", elementId);
swipeMap.put("direction", direction);
swipeMap.put("percent", percent);
driver.executeScript("mobile: swipeGesture", swipeMap);
}
}
Android 身份验证和相机模拟 - C#.Net
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing;
using Appium_Simulations.Resources;
using OpenQA.Selenium.Remote;
namespace Appium_Simulations
{
public class Android
{
public static AndroidDriver driver;
public static String DL_SERVER = "http://digitallab-server:8080";
//Update the below variable to indicate whether you want to use Appium v2 or the default Appium v1
private static Boolean useAppiumV2 = true;
static void Main(string[] args)
{
AppiumOptions appiumOptions = new AppiumOptions();
//Digital Lab embedded Appium capabilities
appiumOptions.AddAdditionalAppiumOption("appium:udid", "8BSX1EDTD");
appiumOptions.AddAdditionalAppiumOption("appium:appPackage", "com.Advantage.aShopping");
appiumOptions.AddAdditionalAppiumOption("appium:appActivity", "com.Advantage.aShopping.SplashActivity");
appiumOptions.AddAdditionalAppiumOption("dl:oauthClientId", "oauth2-UNmB6dXe4XNzJpjY4GSg@opentext.com");
appiumOptions.AddAdditionalAppiumOption("dl:oauthClientSecret", "3Rd1wgB0g71KBT6S2tv3");
appiumOptions.AddAdditionalAppiumOption("dl:tenantId", "999999999");
//Simulations are supported only on packaged apps. The below capability instructs Digital Lab to install the packaged version of the app. Default value is false
appiumOptions.AddAdditionalAppiumOption("dl:installPackagedApp", true);
System.Uri url = new System.Uri(string.Format("{0}/wd/hub", DL_SERVER));
try
{
driver = new AndroidDriver(url, appiumOptions);
System.Console.WriteLine("Digital Lab session was successfully created [Android device]");
//Implicit wait
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
driver.FindElement(MobileBy.Id("imageViewMenu")).Click();
driver.FindElement(MobileBy.Id("textViewMenuUser")).Click();
WebElement username = driver.FindElement(MobileBy.XPath("//*[@resource-id='com.Advantage.aShopping:id/AosEditTextLoginUserName']/android.widget.EditText[1]"));
username.Click();
username.SendKeys("Mercury");
WebElement password = driver.FindElement(MobileBy.XPath("//*[@resource-id='com.Advantage.aShopping:id/AosEditTextLoginPassword']/android.widget.EditText[1]"));
password.Click();
password.SendKeys("Mercury");
driver.FindElement(MobileBy.Id("buttonLogin")).Click();
Thread.Sleep(3000);
String message = driver.FindElement(MobileBy.Id("android:id/message")).Text;
if (message.Contains("Would you like to use fingerprint authentication for logging in ?"))
{
driver.FindElement(MobileBy.Id("android:id/button1")).Click();
//Perform biometric simulation
Console.WriteLine("Biometric simulation result:" + BiometricSimulation("Success", ""));
}
driver.FindElement(MobileBy.AndroidUIAutomator("new UiSelector().textContains(\"LAPTOPS\")")).Click();
driver.FindElement(MobileBy.AndroidUIAutomator("new UiSelector().textContains(\"HP CHROMEBOOK 14 G1(ENERGY STAR)\")")).Click();
int requiredSwipes = driver.FindElements(MobileBy.XPath("//*[@resource-id='com.Advantage.aShopping:id/linearLayoutImagesLocation']/android.widget.FrameLayout")).Count;
WebElement imageList = driver.FindElement(MobileBy.Id("viewPagerImagesOfProducts"));
for (int i = 1; i <= requiredSwipes; i++)
{
swipeElement(imageList.Location, imageList.Size, "up", 1);
}
//load the photo before opening the camera
Console.WriteLine("Photo simulation result:" + CameraSimulation(SimulationResources.cat, ImageFormat.Png, contentType: "image", fileName: "cat.png", action: "camera"));
imageList.Click();
//The steps below are performed on the device's camera and correspond to a device with Android 12.
//These steps may change depending on the device model/brand since the camera app might be different.
driver.FindElement(MobileBy.AccessibilityId("Take photo")).Click();
driver.FindElement(MobileBy.AccessibilityId("Done")).Click();
//Barcode simulation sample usage:
//Console.WriteLine(CameraSimulation(SimulationResources.QRCode, ImageFormat.Png, contentType: "image", fileName: "QRCode.png", action: "barcode"));
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
finally
{
if (driver != null)
driver.Quit();
}
}
/// <summary>
/// Biometric authentication simulation enable/update command.
/// </summary>
/// <param name="authResult">The simulate result, including Failure, Success, Cancel</param>
/// <param name="authResultDetails">
/// The simulate reason when Failure/Cancel
/// Failure: NotRecognized, Lockout, FingerIncomplete(Android only), SensorDirty(Android only), NoFingerprintRegistered(iOS only)
/// Cancel: System, User
/// </param>
/// <returns>The result of ExecuteScript</returns>
public static string BiometricSimulation(string authResult, string authResultDetails = "")
{
Dictionary<string, object> sensorSimulationMap = new Dictionary<string, object>();
Dictionary<string, string> simulationData = new Dictionary<string, string>
{
{ "authResult", authResult },
{ "authType", "Fingerprint" },
{ "authResultDetails", authResultDetails }
};
sensorSimulationMap.Add("simulationData", simulationData);
sensorSimulationMap.Add("action", "authentication");
string simulationResult = JsonConvert.SerializeObject(driver.ExecuteScript("mc:sensorSimulation", sensorSimulationMap));
return JToken.Parse(simulationResult)["message"].ToString();
}
/// <summary>
///
/// </summary>
/// <param name="picture"></param>
/// <param name="format"></param>
/// <param name="contentType">image</param>
/// <param name="fileName"></param>
/// <param name="action">Action: barcode, camera</param>
/// <returns></returns>
public static string CameraSimulation(Bitmap picture, ImageFormat format, string contentType, string fileName, string action)
{
MemoryStream ms = new MemoryStream();
picture.Save(ms, format);
Byte[] bytes = ms.ToArray();
string encodeString = Convert.ToBase64String(bytes);
Dictionary<string, string> sensorSimulationMap = new Dictionary<string, string>
{
{ "uploadMedia", encodeString },
{ "contentType", contentType },
{ "fileName", fileName },
{ "action", action }
};
string simulationResult = JsonConvert.SerializeObject(driver.ExecuteScript("mc:sensorSimulation", sensorSimulationMap));
return JToken.Parse(simulationResult)["message"].ToString();
}
/// <summary>
/// Performs swipe on element
/// </summary>
/// <param name="location">Object location</param>
/// <param name="size">Object size</param>
/// <param name="direction">Swipe direction. Mandatory value. Acceptable values are: up, down, left and right (case insensitive)</param>
/// <param name="percent">The size of the swipe as a percentage of the swipe area size. Valid values must be float numbers in range 0..1, where 1.0 is 100%</param>
private static void swipeElement(Point location, Size size, String direction, double percent)
{
Dictionary<string, object> swipeMap = new Dictionary<string, object>
{
{ "left", 0},
{ "top", 0},
{ "width", size.Width },
{ "height", size.Height + location.Y - 30},
{ "direction", direction },
{ "percent", percent }
};
driver.ExecuteScript("mobile: swipeGesture", swipeMap);
}
}
}
有关其他代码示例,请参阅 Appium 集成和 Appium 代码示例 - Java