Appium 多机并行执行测试(基于Selenium Grid)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TalorSwfit20111208/article/details/53487759

工作原理:

    由于Appium本身就是基于Selenium WebDrive的实现的,而Selenium Grid就是实现Selenium的并发测试的,以下是Selenium的官方的介绍:Selenium-Grid allows you run your tests on different machines against different browsers in parallel. That is, running multiple tests at the same time against different machines running different browsers and operating systems. Essentially, Selenium-Grid support distributed test execution. It allows for running your tests in a distributed test execution environment.http://docs.seleniumhq.org/docs/07_selenium_grid.jsp#what-is-selenium-grid

因此利用Selenium Grid实现Appium的并发测试也是可行的。只需要在Selenium Grid中注册多个Appium Server (WebDriver)在不同的端口上监听不同的手机端口就行(相关参数可以在json文件中配置)以下是Appium官方文档:http://appium.io/slate/en/1.5.3/?java#selenium-grid

Selenium Grid

You are able to register your appium server with a local Selenium grid (setup docs) by using the --nodeconfig server parameter.

> appium --nodeconfig /path/to/nodeconfig.json

# or, if running from source:

> node . --nodeconfig /path/to/nodeconfig.json

In the node config file you have to define the browserName, version and platform and based on these parameters the grid will re-direct your test to the right device. You will also need to configure your host details and the selenium grid details. For a full list of all parameters and descriptions look here

Once you start the appium server and it registers with the grid, you will see your device on the grid console page:

http://<grid-ip-adress>:<grid-port>/grid/console

GRID NODE CONFIGURATION EXAMPLE JSON FILE

{

  "capabilities":

      [

        {

          "browserName": "<e.g._iPhone5_or_iPad4>",

          "version":"<version_of_iOS_e.g._7.1>",

          "maxInstances": 1,

          "platform":"<platform_e.g._MAC_or_ANDROID>"

        }

      ],

  "configuration":

  {

    "cleanUpCycle":2000,

    "timeout":30000,

    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",

    "url":"http://<host_name_appium_server_or_ip-address_appium_server>:<appium_port>/wd/hub",

    "host": <host_name_appium_server_or_ip-address_appium_server>,

    "port": <appium_port>,

    "maxSession": 1,

    "register": true,

    "registerCycle": 5000,

    "hubPort": <grid_port>,

    "hubHost": "<Grid_host_name_or_grid_ip-address>"

  }

}

Valid platforms are listed here

If url, host, and port are not given, the config will be auto updated to point to localhost:whatever-port-Appium-started-on.

If your Appium server is running on a different machine to your Selenium Grid server, make sure you use an external name/IP address in your host & url docs; localhost and 127.0.0.1 will prevent Selenium Grid from connecting correctly.

实现步骤:

Ø 配置node主要是用于注册Appium Server(本地)监听端口、Selenium Grid的监听地址等,示例如下:

{

    "capabilities":

 [

   {

     "browserName": "Android",

          "version":"4.2",

          "maxInstances": 3,

          "platform":"ANDROID",

  "deviceName":"QO4413C21817"//这个是手机的udidadb devices显示的那个串号

  }],

    "configuration":

   {

    "nodeTimeout":120,

    "port":4723,

    "hubPort":4444,

"proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",

    "url":"http://127.0.0.1:4723/wd/hub",

"hub": "127.0.0.1:4444/grid/register",

    "hubHost":"127.0.0.1",

    "nodePolling":2000,

    "registerCycle":10000,

    "register":true,

    "cleanUpCycle":2000,

    "timeout":30000,

    "maxSession":5

 }

   }

多个Appium Server需要配个多个node配置文件,其中Appium Server的注册地址不能相同,不然的话只会启动一个Appium Server

Ø 启动 Selenium Server

命令如下:java -jar selenium-server-standalone-2.44.0.jar -role hub

你会看到以下输出

 

然后在浏览器输入注册的地址可以看到Selenium Grid的控制台,如下:

 

Ø 编写测试脚本

如下:

package com.appium.test;

import java.net.MalformedURLException;

import java.net.URL;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.List;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.testng.Assert;

import org.testng.annotations.AfterTest;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.Parameters;

import org.testng.annotations.Test;

import io.appium.java_client.MobileElement;

import io.appium.java_client.android.AndroidDriver;

import io.appium.java_client.remote.MobileCapabilityType;

public class ParallelTest {

public AndroidDriver<MobileElement> driver;

public static DesiredCapabilities cap;

SimpleDateFormat current_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

String initial_value = "未登录", input_value = "apple00", before_login,

afeter_login;

@BeforeTest(alwaysRun = true)

@Parameters({ "port", "device" })

public void testsetup(String port, String device)

throws MalformedURLException, InterruptedException {


cap = new DesiredCapabilities();

cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");

cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, "5.0.1");

cap.setCapability(MobileCapabilityType.DEVICE_NAME, device);

cap.setCapability("noReset", "true");

//cap.setCapability(MobileCapabilityType.APP, file);

cap.setCapability("appPackage", "com.updrv.lifecalendar");

cap.setCapability("appActivity", ".activity.MainActivity");

driver = new AndroidDriver<>(new URL("http://127.0.0.1:" + port

+ "/wd/hub"), cap);

System.out.println("session id is---" + driver.getSessionId());

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

}

@Test

public void Login() throws InterruptedException {

WebElement d = driver

.findElement(By

.id("com.updrv.lifecalendar:id/lin_menu_main_personal_account"));

d.click();

// 判断是否为登录状态

before_login = driver

.findElement(

By.id("com.updrv.lifecalendar:id/tv_personal_account_user_name"))

.getText();// 适用于索尼手机

// 用户未登录直接进入注册页面登录

if (before_login.equals(initial_value)) {

System.out.println("用户未登录进入登录页面");

WebElement account = driver.findElement(By

.id("com.updrv.lifecalendar:id/account_linear"));

account.click();

WebElement user_accounts = driver.findElement(By

.id("com.updrv.lifecalendar:id/user_accounts"));

user_accounts.sendKeys(input_value);

WebElement user_password = driver.findElement(By

.id("com.updrv.lifecalendar:id/user_password"));

user_password.sendKeys("123456");

WebElement btn_login = driver.findElement(By

.id("com.updrv.lifecalendar:id/btn_login"));

btn_login.click();

// Thread.sleep(2000);

afeter_login = driver

.findElement(

By.id("com.updrv.lifecalendar:id/tv_personal_account_user_name"))

.getText();

System.out.print(current_time.format(new Date()) + " ");

System.out.println("定位用户名成功状态为:" + afeter_login + " ");

if (afeter_login.equals(input_value)) {

System.out.print(current_time.format(new Date()) + " ");

System.out.println("当前用户登录状态为:登录成功");

} else if (afeter_login.equals(initial_value)) {

System.out.print(current_time.format(new Date()) + " ");

System.out.println("用户未登录");

}

} else {

System.out.println("用户已登录退出登录再登录");

WebElement e = driver.findElement(By

.id("com.updrv.lifecalendar:id/account_linear"));

e.click();

WebElement e1 = driver.findElement(By

.id("com.updrv.lifecalendar:id/account_linear"));

e1.click();

List<MobileElement> textFieldsList = driver

.findElementsByClassName("android.widget.RelativeLayout");

textFieldsList.get(4).click();

WebElement e2 = driver.findElement(By

.id("com.updrv.lifecalendar:id/dialog_text2"));

e2.click();

WebElement account = driver.findElement(By

.id("com.updrv.lifecalendar:id/account_linear"));

account.click();

WebElement user_accounts = driver.findElement(By

.id("com.updrv.lifecalendar:id/user_accounts"));

user_accounts.sendKeys(input_value);

WebElement user_password = driver.findElement(By

.id("com.updrv.lifecalendar:id/user_password"));

user_password.sendKeys("123456");

WebElement btn_login = driver.findElement(By

.id("com.updrv.lifecalendar:id/btn_login"));

btn_login.click();

afeter_login = driver

.findElement(

By.id("com.updrv.lifecalendar:id/tv_personal_account_user_name"))

.getText();

System.out.print(current_time.format(new Date()) + " ");

System.out.println("定位用户名成功状态为:" + afeter_login + " ");

if (afeter_login.equals(input_value)) {

System.out.print(current_time.format(new Date()) + " ");

System.out.println("当前用户登录状态为:登录成功");

} else if (afeter_login.equals(initial_value)) {

System.out.print(current_time.format(new Date()) + " ");

System.out.println("用户未登录");

}

}

}

@AfterTest

public void quit() {

driver.quit();

}

}

Ø 编写testng.xml配置文件

该配置文件主要是为了参数化测试过程,是并发测试实现的一个关键点,示例如下:

<suite name="SmokeSuite" verbose="1" parallel="tests"

thread-count="5">

<!-- <listeners> <listener class-name="org.uncommons.reportng.HTMLReporter"

/> <listener class-name="org.uncommons.reportng.JUnitXMLReporter" /> </listeners> -->

<tests>

<test name="run on device 1">

<classes>

<parameter name="port" value="4723" />

<parameter name="device" value="QO4413C21817"></parameter>

                <parameter name="bp" value="4730"></parameter>

<class name="com.appium.test.ParallelTest">

<methods>

<include name="Login" />//包含的测试方法

<exclude name="InvalidValidLoginTest,ValidLoginTest" />//不包含的测试方法

</methods>

</class>

</classes>

</test>

<test name="run on device 2">

<classes>

<parameter name="port" value="4725"></parameter>

<parameter name="device" value="Coolpad8750-0xa36065"></parameter>

<parameter name="bp" value="4732"></parameter>

<class name="com.appium.test.ParallelTest">

<methods>

<include name="Login" />

<exclude name="InvalidValidLoginTest,ValidLoginTest" />

</methods>

</class>

</classes>

</test>

</tests>

</suite>

Ø 启动Appium Server

如下图,分别在不同的端口上启动了2Appium Server

 

Ø 运行测试

使用testng运行testng.xml文件即可实现并发测试

Ø 查看测试结果

Testng会生成JunitHTML报告,如下

 


Jenkins持续集成

通过selenium grid我们很容易实现与Jenkins的集成。Jenkins插件里有一个Selenium Grid,通过这个插件我们可以完成本地或者远程的多机并发测试。下面我们来详细看下一下如何实现。

1、Jenkins插件管理中下载Selenium Grid Jenkins Selenium Plugin Selenium Capability Axis这几个插件。下载安装完毕后,Jenkins主界面会显示Selenium Grid选项如下图:

 

2、再进入系统设置中设置Selenium 相关的参数,如下图

 

其中

Selenium Hub Port 

表示Selenium Grid 的服务运行的中心节点(上文有提到),一般默认是4444,如果此端口没有被占用一般不需要更改。

Hostname

表示主机地址,默认是在Jenkins URL上运行,如果要在非主机上运行则可以选择使用“Use a fixed hostname”这个选项

Selenium Grid Server

表示selenium Girder Server运行的端口,可以指定为非本机地址

3、进入job中配置构建。

这里与上面有点不一样,不过原理都是一样的,都是要把测试的设备注册到Selenium Grid Server节点上。由于引用maven或者ant脚本容易被Jenkins杀死进程,暂时没找到解决的办法,可以直接用window脚本来注册,代码如下:

@echo off

@ rem adb shell pm clear com.updrv.lifecalendar

@start appium -a 127.0.0.1 -p 4723  --bootstrap-port 4730 -U 7N2RDQ1496009372 --nodeconfig  %~dp0jsonFiles\nodeconfig_1.json

@start appium -a 127.0.0.1 -p 4725 --bootstrap-port 4732 -U Coolpad8720L-0x00a36065 --nodeconfig  %~dp0jsonFiles\nodeconfig_2.json

mvn test

注意一定要在不同的窗口来注册节点设备,不然Jenkins会报错,最后让maven日志显示在Jenkins日志输出上。

猜你喜欢

转载自blog.csdn.net/TalorSwfit20111208/article/details/53487759