我目前正在为我公司的移动测试重写自动化测试框架。我们正在尝试使用一个接口,该接口由多个页面对象模型实现,这些接口模型依赖于运行应用程序的移动设备的操作系统。我可以让这个框架按顺序运行,甚至创建多个线程,但无论我做什么,它都不会并行运行。值得注意的是,我们使用Appium和一些名为DeviceCart
/ DeviceConnect
的东西,它允许我物理地远程进入多个设备,因此这不是在网格上运行。有了这个说我会链接我的相关代码(这是我的同一代码的第二个版本,我写了一个和一个没有使用ThreadLocal
)
这应该为每个Test实例化一个带有新线程的新驱动程序
public class TLDriverFactory {
private ThreadLocal < AppiumDriver < MobileElement >> tlDriver = new ThreadLocal <>();
public synchronized void setTLDriver(OS platform, String server, String udid, String bundleID) {
switch (platform) {
case IOS:
tlDriver = ThreadLocal.withInitial(() -> {
try {
return new IOSDriver < MobileElement > (new URL(server), DesiredCapsManager.getDesiredCapabilities(OS.IOS, udid, bundleID));
} catch(MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
break;
case ANDROID:
tlDriver = ThreadLocal.withInitial(() -> {
try {
return new AndroidDriver < MobileElement > (new URL(server), DesiredCapsManager.getDesiredCapabilities(OS.ANDROID, udid, bundleID));
} catch(MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
break;
default:
break;
}
}
public synchronized ThreadLocal < AppiumDriver < MobileElement >> getTLDriver() {
return tlDriver;
}
}
这会处理浏览器功能
public class DesiredCapsManager {
public static DesiredCapabilities getDesiredCapabilities(OS platform, String udid, String bundleID) {
//Set DesiredCapabilities
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceConnectUserName", "[email protected]");
capabilities.setCapability("deviceConnectApiKey", "API-Token-Here");
capabilities.setCapability("udid", udid);
capabilities.setCapability("platformName", platform);
capabilities.setCapability("bundleID", bundleID);
//IOS only Settings
if (platform.equals(OS.IOS)) {
capabilities.setCapability("automationName", "XCUITest");
}
else {
//Android only Settings
capabilities.setCapability("automationName", "appium");
}
return capabilities;
}
}
这是每个测试继承的Base Test类
public class BaseTest {
protected AppiumDriver < MobileElement > driver;
protected AppiumSupport.TLDriverFactory TLDriverFactory = new AppiumSupport.TLDriverFactory();
public enum OS {
ANDROID,
IOS
}
@AfterMethod
public synchronized void tearDown() throws Exception {
driver.quit();
TLDriverFactory.getTLDriver().remove();
}
}
这是测试用例本身
public class Test_SignIn extends BaseTest {
protected SignInPage signInPage;
@Parameters(value = {
"udid",
"bundleID",
"platform",
"server"
})
@BeforeMethod
public void setup(String udid, String bundleID, OS platform, String server) throws MalformedURLException,
InterruptedException {
//Set & Get ThreadLocal Driver
TLDriverFactory.setTLDriver(platform, server, udid, bundleID);
driver = TLDriverFactory.getTLDriver().get();
Thread.sleep(5000);
switch (platform) {
case IOS:
signInPage = new SignInPageIOS(driver);
break;
case ANDROID:
signInPage = new SignInPageAndroid(driver);
break;
default:
break;
}
System.out.println("Current Thread ID BeforeTest: " + Thread.currentThread().getName());
}
@Test
public synchronized void Authenticate() throws Exception {
System.out.println("Current Thread ID Test 1: " + Thread.currentThread().getName());
signInPage.Login("Username", "Password");
}
}
这是testng.xml文件
< !DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Test" parallel="tests" thread-count="4">
<test name="SignIn" parallel ="instances" thread-count="2">
<parameter name="udid" value="DeviceIdGoesHere" />
<parameter name="bundleID" value="Environment.address.here" />
<parameter name="platform" value="ANDROID" />
<parameter name="server" value="http://deviceconnect/appium" />
<classes>
<class name="Test.Test_SignIn">
</class>
</classes>
</test>
<test name="SignIn2" parallel="instances" thread-count="2">
<parameter name="udid" value="DeviceIdGoesHere" />
<parameter name="bundleID" value="Environment.address.here" />
<parameter name="platform" value="IOS" />
<parameter name="server" value="http://deviceconnect/appium" />
<classes>
<class name="Test.Test_SignIn">
</class>
</classes>
</test>
</suite>
我正在寻找的是,如果有人能够确定我犯了什么错误或者瓶颈是什么阻止了测试并行运行
根据您目前共享的内容,这里是清理和修复的代码,应该支持您的并发要求。
Driver工厂类负责为每个线程创建和清理Appium驱动程序实例,如下所示:
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import java.net.MalformedURLException;
import java.net.URL;
public class TLDriverFactory {
private static final ThreadLocal<AppiumDriver<MobileElement>> tlDriver = new ThreadLocal<>();
public static void setTLDriver(BaseTest.OS platform, String server, String udid, String bundleID) throws MalformedURLException {
System.out.println("Current Thread ID Driver Instantiation: " + Thread.currentThread().getName());
AppiumDriver<MobileElement> driver;
switch (platform) {
case IOS:
driver = new IOSDriver<>(new URL(server), DesiredCapsManager.getDesiredCapabilities(BaseTest.OS.IOS, udid, bundleID));
break;
default:
driver = new AndroidDriver<>(new URL(server), DesiredCapsManager.getDesiredCapabilities(BaseTest.OS.ANDROID, udid, bundleID));
break;
}
tlDriver.set(driver);
}
public static AppiumDriver<MobileElement> getTLDriver() {
return tlDriver.get();
}
public static void cleanupTLDriver() {
tlDriver.get().quit();
tlDriver.remove();
}
}
以下是我猜测的BaseTest应该是所有测试的基类,看起来像
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
public class BaseTest {
private static final ThreadLocal<SignInPage> signInPage = new ThreadLocal<>();
public enum OS {
ANDROID,
IOS
}
@Parameters(value = {"udid", "bundleID", "platform", "server"})
@BeforeMethod
public void setup(String udid, String bundleID, OS platform, String server) throws Exception {
//Set & Get ThreadLocal Driver
TLDriverFactory.setTLDriver(platform, server, udid, bundleID);
Thread.sleep(5000);
SignInPage instance;
switch (platform) {
case IOS:
instance = new SignInPageIOS(TLDriverFactory.getTLDriver());
break;
default:
instance = new SignInPageAndroid(TLDriverFactory.getTLDriver());
break;
}
System.out.println("Current Thread ID BeforeTest: " + Thread.currentThread().getName());
signInPage.set(instance);
}
@AfterMethod
public void tearDown() {
System.out.println("Current Thread ID AfterTest: " + Thread.currentThread().getName());
TLDriverFactory.cleanupTLDriver();
}
protected static SignInPage getPageForTest() {
return signInPage.get();
}
}
以下是页面类的构造函数的外观
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
public class SignInPageIOS extends SignInPage {
public SignInPageIOS(AppiumDriver<MobileElement> tlDriver) {
super(tlDriver);
}
}
以下是典型测试用例的外观
import org.testng.annotations.Test;
public class Test_SignIn extends BaseTest {
@Test
public void authenticate() {
//Get the instance of "SignInPage" for the current thread and then work with it.
getPageForTest().Login("Username", "Password");
}
}