摘要:
本文档涵盖了自动化测试的关键概念,特别是使用 Maven、TestNG、Appium 和 Jenkins 的集成测试流程。TestNG 是一个强大的Java测试框架,支持注解驱动、测试套件、测试组和数据驱动测试。Appium 是一个跨平台的移动自动化工具,允许通过其Inspector工具进行元素定位和执行各种操作。PageObject 设计模式用于提高测试代码的可维护性。ExtentReports 提供了详细的测试报告。Jenkins 是一种持续集成工具,可以自动化测试项目的构建和部署。本文档详细介绍了如何使用这些工具进行自动化测试用例的开发、执行和集成,包括环境搭建、元素识别、错误处理和报告生成。
文章目录
- 摘要:
- 第一章. TestNG框架
- 第二章. Appium基础知识
- 第三章. Appium元素识别与定位
- 第四章. Appium基本操作
- 第五章. Appium等待机制
- 第六章. PageObject设计模式
- 第七章. 测试报告简介
- 第八章. Jenkins可持续集成开发
- 第十章. 扩展{#todo3}
第一章. TestNG框架
1. TestNG框架简介
TestNG是一款强大的自动化测试框架,类似于JUnit 和NUnit。
TestNG专为Java应用程序设计。
TestNG 提供了广泛的功能,包括单元测试、功能测试、端到端测试、集成测试等。TestNG
2. TestNG框架原理
TestNG 框架的核心原理基于以下几个方面:
2.1 注解驱动:
TestNG 使用注解(Annotations)来标记测试类和方法,这样编译器和运行时环境可以自动识别哪些是测试代码。常见的注解包括:
- @Test: 标记测试方法。
- @BeforeSuite, @AfterSuite: 在整个测试套件之前或之后执行。
- @BeforeTest, @AfterTest: 在每个测试前或后执行。
- @BeforeClass, @AfterClass: 在每个测试类前或后执行。
- @BeforeMethod, @AfterMethod: 在每个测试方法前或后执行
2.2 测试执行生命周期:
TestNG 的执行生命周期是按照测试方法的依赖和生命周期注解的顺序进行的。生命周期方法确保了正确的初始化和清理顺序,如下所示:
- @BeforeSuite:在所有测试之前运行一次。
- @BeforeTest:在每个测试之前运行,但只在其所属的 @Test 或 @Configuration 方法之前运行。
- @BeforeClass:在每个测试类之前运行,但只在其所属的测试类中的方法之前运行。
- @BeforeMethod:在每个测试方法之前运行。
- @AfterMethod:在每个测试方法之后运行。
- @AfterClass:在每个测试类之后运行,但只在其所属的测试类中的所有方法之后运行。
- @AfterTest:在每个测试之后运行,但只在其所属的 @Test 或 @Configuration 方法之后运行。
- @AfterSuite:在所有测试之后运行一次。
package com.exercise;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.annotations.*;
import org.testng.annotations.Test;
public class TestLifeCycleTest {
private int beforeSuiteCount = 0;
private int afterSuiteCount = 0;
private int beforeTestCount = 0;
private int afterTestCount = 0;
private int beforeClassCount = 0;
private int afterClassCount = 0;
private int beforeMethodCount = 0;
private int afterMethodCount = 0;
@BeforeSuite
public void beforeSuite() {
beforeSuiteCount++;
System.out.println("Before Suite");
}
@AfterSuite
public void afterSuite() {
afterSuiteCount++;
System.out.println("After Suite");
}
@BeforeTest
public void beforeTest() {
beforeTestCount++;
System.out.println("Before Test");
}
@AfterTest
public void afterTest( ) {
afterTestCount++;
System.out.println("After Test");
}
@BeforeClass
public void beforeClass() {
beforeClassCount++;
System.out.println("Before Class");
}
@AfterClass
public void afterClass() {
afterClassCount++;
System.out.println("After Class");
}
@BeforeMethod
public void beforeMethod() {
beforeMethodCount++;
System.out.println("Before Method");
}
@AfterMethod
public void afterMethod(ITestResult result) {
afterMethodCount++;
System.out.println("After Method");
}
@Test
public void testMethod() {
Assert.assertEquals(beforeSuiteCount, 1, "Before Suite count is incorrect");
Assert.assertEquals(afterSuiteCount, 0, "After Suite count is incorrect");
Assert.assertEquals(beforeTestCount, 1, "Before Test count is incorrect");
Assert.assertEquals(afterTestCount, 0, "After Test count is incorrect");
Assert.assertEquals(beforeClassCount, 1, "Before Class count is incorrect");
Assert.assertEquals(afterClassCount, 0, "After Class count is incorrect");
Assert.assertEquals(beforeMethodCount, 1, "Before Method count is incorrect");
Assert.assertEquals(afterMethodCount, 0, "After Method count is incorrect");
System.out.println("Running Test Method");
}
}
2.3 并行测试:
TestNG 支持并行执行测试,这意味着可以同时运行多个测试方法,以提高测试效率。这可以通过配置 @Test 注解的 threadPoolSize 参数来实现。
2.4 测试分组:
通过 groups 属性,可以将测试方法分组,并使用 @Test(groups={“group1”, “group2”}) 来指定要运行的测试组。这允许按需执行特定的测试集。
2.5 依赖管理:
使用 @Test 注解的 dependsOnMethods 和 dependsOnGroups 参数,可以控制测试方法之间的依赖关系,确保测试按照特定的顺序执行。
2.6 数据适配器:
@DataProvider 注解允许从外部数据源(如文件或数据库)提供测试数据,这样测试方法可以针对多组输入数据进行重复执行。
2.7 异常断言:
通过 expectedExceptions 参数,可以在 @Test 注解中声明期望的异常类型,确保测试方法抛出正确的异常。
2.8 报告生成:
TestNG 自带报告生成功能,可以生成详细的测试报告,包括成功的测试、失败的测试、跳过的测试等信息,这对于分析测试结果非常有用。
2.9 XML配置文件:
通过 testng.xml 文件,可以定义测试套件、测试类和测试方法的执行顺序,以及其他的配置信息,方便进行批量测试的管理和执行。
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="SuiteName" verbose="1">
<listeners>
<!-- 添加自定义监听器 -->
</listeners>
<test name="TestName">
<parameter name="paramName" value="paramValue" />
<classes>
<class name="com.example.YourTestClass">
<methods>
<include name="specificMethodName" />
</methods>
</class>
</classes>
</test>
<test name="AnotherTestName">
<!-- 另一个测试 -->
</test>
<test name="ParameterizedTest">
<parameter name="paramName" value="paramValue" />
<classes>
<class name="com.example.ParameterizedTestClass">
<methods>
<exclude name="excludeMethodName" />
</methods>
</class>
</classes>
<data-provider name="dataProviderName">
<!-- 数据提供器定义 -->
</data-provider>
</test>
<test name="ParallelTest">
<parallel>methods</parallel>
<classes>
<!-- 并行执行的测试类 -->
</classes>
</test>
<suite-files>
<!-- 引入其他XML配置文件 -->
<suite-file path="relative_path_to_suite_file.xml" />
</suite-files>
<listeners>
<!-- 添加内置或自定义的监听器 -->
<listener class-name="org.uncommons.reportng.HTMLReporter"/>
<listener class-name="org.uncommons.reportng.JUnitXMLReporter"/>
</listeners>
</suite>
<!--
以下是一些关键元素的说明:
<suite>: 测试套件,可以包含多个<test>标签,每个<test>代表一个独立的测试集。
name: 测试套件的名称。
verbose: 控制输出的详细程度,值越大,输出信息越多。
<test>: 测试集,可以包含一个或多个<class>标签。
name: 测试集的名称。
<parameter>: 测试级别的参数,可以被测试类或方法使用。
<classes>: 定义要执行的测试类。
<class>: 测试类,可以包含<methods>标签来指定要运行的方法。
name: 测试类的全限定名。
<methods>: 用于指定要运行或排除的方法。
<include>: 指定要运行的方法名。
<exclude>: 排除的方法名。
<data-provider>: 数据提供器,用于为测试方法提供数据。
name: 数据提供器的名称,将在测试方法中引用。
数据提供器的定义通常包含在类内部,但可以在XML中定义,以便在多个测试中复用。
<parallel>: 控制测试的并发执行,可以是false、methods、tests、classes或instances。
<suite-files>: 包含其他testng.xml文件,使得配置文件可以模块化。
<listeners>: 添加监听器,可以是TestNG内置的或自定义的,用于扩展TestNG的行为,如报告生成、日志记录等。
在实际使用中,testng.xml文件可以根据需要进行扩展和定制,以满足特定的测试需求。
-->
2.10 Maven 和 Gradle 等集成:
TestNG 可以轻松地与构建工具如 Maven 和 Gradle 集成,使得测试成为构建过程的一部分。TestNG 与 Jenkins 结合使用,可以实现自动化测试的持续集成,确保每次代码变更后都能快速验证其正确性,详见IX章节Jenkins可持续集成开发。
2.11 插件支持:
在流行的 IDEs,如 IntelliJ IDEA 和 Eclipse 中,都有 TestNG 插件,提供了便捷的测试编写、运行和调试支持。
3. TestNG框架使用
3.1 TestNG的安装
安装TestNG既可以使用maven安装也可以使用插件进行安装。具体如下:
3.1.1 Maven安装
使用maven进行安装,则在pom.xml中加入依赖如下:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.10</version>
<scope>test</scope>
</dependency>
3.1.2 插件安装
这里使用eclipse版本为:eclipse-jee-2021-12-R-win32-x86_64.zip。
具体操作:点击Eclipse的Help–>Eclipse Marketplace–>Query[TestNG],如下图所示:
TestNG安装完成之后,便可使用TestNG运行Test程序了,如下图所示:
3.2 TestNG基本使用
使用TestNG测试两数相加的方法。
编写一个Addition类,实现两数相加:
public class Addition {
public int add(int num1, int num2) {
return num1 + num2;
}
}
编写一个测试类,提供六组测试数据,进行测试:
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class AdditionTest {
private Addition addition = new Addition();
@DataProvider(name = "additionData")
public Object[][] provideAdditionData() {
return new Object[][] {
{ 1, 2, 3 },
{ 5, 7, 12 },
{ -3, 8, 5 },
{ 0, 0, 0 },
{ Integer.MAX_VALUE, 1, Integer.MAX_VALUE + 1 },
{ 10, -10, 0 },
{ 200, 300, 500 },
{ -100, -200, -300 },
{ 12345, 67890, 79235 },
{ -5000, 5000, 0 }
};
}
@Test(dataProvider = "additionData")
public void testAddition(int num1, int num2, int expectedSum) {
Assert.assertEquals(addition.add(num1, num2), expectedSum);
}
}
最终测试结果如下:
以上是对TestNG的简单使用,其中对生命周期,xml配置的操作,这里不再演示,具体见TestNG框架原理中详解对象文档。
第二章. Appium基础知识
1. Appium简介
appium是一个自动化测试开源工具,支持IOS平台和Android平台上的原生应用,web应用和混合应用。
2. Appium组成及运行原理
组成:
- ① Appium Server
Appium是一个C/S结构,包括Server和Client。Appium Server是Appium的服务端,作为一个Web接口服务,使用Node.js实现。
- ②Appium Desktop
Appium Server的图像界面,可设置启动or停止服务器、查看日志,可使用inspect查看应用程序的元素。
- ③Appium GUI
它是Appium Desktop的前身,也是把Appium Server封装成一个图像界面。该产品的Windows版本在2015年的AppiumForWindows_1_4_16_1.zip之后就停止更新了。目前版本可以使用,但是封装的不是最新的Appium版本,而是1.4.16版本。如果要使用最新的桌面版需要使用Appium Desktop。
- ④Appium Client
Appium Client作为客户端,会给服务端Appium Server端发送请求会话来执行自动化任务。类似使用浏览器访问网页,可使用不同的客户端浏览器(IE/Firefox/Chrome…)访问同一个网站,通过操作发送请求到服务器来获取数据。
运行原理:
- ①客户端运行脚本的时候,调用任何的appiumAPI,都会向Appium Server端post一条HTTP请求,请求内容就是根据webdriver wire protocol协议规定的一条JSON格式的数据;
- ②当开启appium服务器的同时就开启了监听端口,Appium Server端接收到请求后,解析出JSON数据并发送到手机端;
- ③手机端上已经由BootStrap.jar(iOS为BootStrip.js)开启的socket服务器监听相应的端口,BootStrap.jar在appium每个session第一次访问手机端的时候会自动安装;
- ④手机端接收到对应的请求后,通过BootStrap.jar翻译成UIAutomator能执行的命令,然后通过UIAutomator处理并操作APP完成测试。
3. Appium-desktop安装
-
①进入官网下载appium-desktop,点击下图箭头所标
官网地址:https://github.com/appium/appium-desktop/releases/tag/v1.21.0 -
②双击Appium-windows-1.21.0.exe,弹出如下图,直接安装即可
-
③双击 ,出现如下图:
-
④配置JDK和SDK
-
⑤重新启动Appium,点击“Start Server v1.21.0”,出现如下图,完成Appium安装
4. Appium-inspector安装
- ①进入官网下载appium-inspector,点击下图箭头所标
官网地址:https://github.com/appium/appium-inspector/releases - ②双击Appium-Inspector-2024.3.4-win-x64.exe,安装默认方式安装即可:
- ③点击Appium Inspector图标启动,如下图显示,即安装成功:
5. Node.js安装
Node.js 是一个开源和跨平台的 JavaScript 运行时环境。 它几乎是任何类型项目的流行工具,Appium是使用Nodejs实现的,所以Nodejs是解释器,首先需要确认安装好
- ①进入node.js官网下载安装包,下载右边稳定版
官网地址:https://nodejs.org/en/ - ②点击node-v20.12.2-x64.msi进行下载默认安装即可
- ③如果cmd中没有npm命令,则需设置环境变量:
在cmd中输入命令rundll32 sysdm.cpl,EditEnvironmentVariables
打开环境变量设置
在path中添加Node的bin路径即可,如下图所示
6. Appium-Doctor安装
appium-doctor 是用来验证 Appium 的所有依赖环境。运行 appium-doctor,然后提供 --ios 或者 --android 参数来验证两个平台的依赖环境是否配置正确,如:JDK、SDK等
-
①打开终端,执行指令:
npm install appium-doctor -g
执行结果如下图:
-
②打开终端,输入指令
appium-doctor --android
,出现如下图片,所有AOS环境安装成功
-
③检查Appium连接Android手机(确保Android手机打开USB调试)
打开终端,输入指令:adb devices
显示有设备即可
7. Appium-client api加载
使用maven工具添加Appium依赖
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>6.1.0</version>
</dependency>
8. 第一个appium-client脚本执行
实现测试代码
声明驱动实例:
AndroidDriver driver =null;
// 获取参数实例,可以对连接的参数进行设置
DesiredCapabilities des=new DesiredCapabilities();
// 正式连接,获取驱动实例:
driver =new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"),des);
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// 通过控件id获取具体的控件实例,并对其操作
WebElement findElement=
driver.findElement(By.id("com.formssi.oa:id/phoneNumberEditText"));
findElement.sendKeys("liee");
// 最后关闭连接
if(driver!=null) {
driver.closeApp();
}
tip
具体代码见/Learn_test/src/main/java/And/learn/TestOne.java
第三章. Appium元素识别与定位
1. Appium-inspector工具使用
- ①手动连接
填写Capabitlity Builder参数,如下图所示
填写Remote Path参数,/wd/hub
填写完毕后,点击Start Session开启会话即可
需要手动设置连接参数(例子):
{
"platformName": "Android",
"appium:platformVersion": "11",
"appium:noReset": "true",
"appium:fullReset": "false",
"appium:deviceName": "fyyhvgdius4dvkfe",
"appium:appPackage": "com.formssi.oa",
"appium:appActivity": "com.formssi.work.main.SplashActivity"
}
需要设置在Appium DeskTop中设定好的本地接口
其中platformName,appium;platformVersion 参数为必填项
参数名 | 类型 | 是否必填 | 描述 |
---|---|---|---|
platformName | string | 是 | 托管应用或浏览器的平台类型 |
appium:automationName | string | 是 | 要使用的 Appium 驱动程序的名称 |
browserName | string | 不 | 要启动和自动化的浏览器的名称(如果驱动程序支持 Web 浏览器作为特殊情况) |
appium:app | string | 不 | 可安装应用程序的路径 |
appium:deviceName | string | 不 | 要自动化的特定设备的名称,例如(目前仅对指定 iOS 模拟器有用,因为在其他情况下,通常建议通过该功能使用特定设备 ID)。 |
appium:platformVersion | string | 不 | 平台的版本,例如,适用于 iOS,16.0 |
appium:newCommandTimeout | number | 不 | Appium 服务器在决定客户端已离开并且会话应关闭之前应等待客户端发送命令的秒数 |
appium:noReset | boolean | 不 | 如果为 true,则指示 Appium 驱动程序在会话启动和清理期间避免其通常的重置逻辑(默认false) |
appium:fullReset | boolean | 不 | 如果为 true,则指示 Appium 驱动程序使用其他步骤来增强其通常的复位逻辑,以确保最大的环境可重现性(默认false) |
appium:eventTimings | boolean | 不 | 如果为 true,则指示 Appium 驱动程序收集事件计时 (默认false) |
appium:printPageSourceOnFindFailure | boolean | 不 | 如果为 true,则收集页面源代码,并在查找元素的请求失败时将其打印到 Appium 日志(默认false) |
更多可以看下面的连接:会话功能 - Appium 文档
使用录制按钮录制脚本
可以直接生产所需要的代码,但可能因为版本问题,部分方法不可用,需要注意
2. 通过控件元素属性定位
2.1 直接使用id:
通过点击手机界面上的控件,获取其具体的控件ID,如下:
获取之后便可以在代码中使用该id来访问此控件。
2.2 间接查询:
但是有的控件没有具体的id,如下图所示:
四个控件使用同一个id,这对我们使用控件有一定的影响,对此,我们可以使用获取其控件列表的方式来访问某一空间,代码如下:
代码待补全{#todo}
(//android.widget.LinearLayout[@resource-id="com.formssi.oa:id/contactLinearLayout"])[1]
// 解析:(//android.widget.LinearLayout[@resource-id="com.formssi.oa:id/contactLinearLayout"])是父级控件
【1】是指第一个子控件
3. 通过classname查询
driver.findElementByClassName("android.widget.TextView");
如下图
第四章. Appium基本操作
1. 单击操作
// 点击坐标(X,Y)
action.tap(PointOption.point(20, 30));
2. 输入操作
// 查询控件的ID,向该控件填写文本信息 00127
driver.find_element(AppiumBy.ID, 'com.jiyong.rta.debug:id/edt_customer_number').send_keys('00127')
3. 清除操作
// 查询控件的ID,向该控件文本信息 清楚
driver.find_element(AppiumBy.ID, 'com.jiyong.rta.debug:id/edt_customer_number').clean()
4. 点击操作
// 查询控件的ID,向该控件文本信息 清楚
driver.find_element(AppiumBy.ID, 'com.jiyong.rta.debug:id/edt_customer_number').click()
5. 键盘操作
当前所使用的包已不支持如下键盘方法:
需要使用java-client-4.1.2包
、selenium-server-standalone-2.53.1.jar包
driver.pressKeyCode(AndroidKeyCode.HOME)
driver.pressKeyCode(4);
6. 元素的状态判断
- isDisplayed()是否可见
- isEnabled()是否可用
- isSelected()是否选中
7. 元素的属性获取
getAttribute(“text”)获取元素text的属性内容
8. 常用手势操作
TouchAction的原理是将一系列的动作放在一个链条中,然后将该链条传递给服务器,服务器接收到该链条后,解析各个动作,逐个执行。
// 初始化TouchAction
// Android使用AndroidTouchAction类
// IOS使用IOSTouchAction类
AndroidTouchAction action = new AndroidTouchAction(driver);
// press按压操作
// 按压点坐标(X,Y)
action.press(PointOption.point(20, 30));
// longPress长按操作
// 长按点坐标(X,Y)
action.longPress(PointOption.point(20, 30));
// tap点击操作
// 点击坐标(X,Y)
action.tap(PointOption.point(20, 30));
// moveTo移动操作
// 以(X,Y)点为目标,从另一个点移动到该目标上
action.moveTo(PointOption.point(20, 30));
// release释放操作,代表该系列动作的一个结束标志
action.release();
// perform执行该动作发送到服务器的命令操作
action.perform();
// TouchAction示例
action.press(PointOption.point(300, 700)).moveTo(PointOption.point(150, -250)).release().perform();
9. 屏幕截图
/**
* 获取当前屏幕截图
* 保存到D:\new\lwj\images这里
*/
File screenShotFile=driver.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenShotFile, new File("D:\\new\\lwj\\images\\shot.png"));
第五章. Appium等待机制
1. 隐式等待
隐式等待是设置一个时间(最长等待时间),在查找元素或元素操作时,如果元素不可用,WebDriver会等待这段时间。
// 设置最长等待时间为10秒
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
2. 显示等待
显式等待是等待特定的条件发生。WebDriver等待一个最长时间(设定的时间),在此期间,它会查看元素是否可用。如果在设定的时间内元素出现,则会继续执行。
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "com.example:id/login_button"))
第六章. PageObject设计模式
1. PO设计模式原理
页面抽象:对于应用中的每个页面,我们创建一个对应的Page Object类。这个类包含了该页面上所有必要的元素的信息。
元素定位:在Page Object类中,为页面上的每个元素定义一个定位方法。定位方法通常使用Appium的定位策略,如id、name、xpath等。
元素操作:定义针对页面元素的操作方法,例如点击按钮、输入文本等。
页面功能:在Page Object类中,封装对页面的操作,实现页面的具体功能,如登录、搜索等。
独立性:测试用例与Page Object类中的具体实现细节隔离,测试用例只需要调用Page Object类的方法即可完成页面的操作。
复用性:相同的Page Object类可以在不同的测试用例中被重复使用,提高了代码的复用性。
维护性:当应用的界面发生变化时,只需要更新对应的Page Object类中的元素定位和方法,而不需要修改测试用例,降低了维护成本。
2. PO模式优点
高可维护性:页面结构变化时,只需修改PO类,测试用例无需变动。
高可读性:测试代码与业务逻辑分离,更加清晰易懂。
高可复用性:同一PO类可被多个测试用例复用。
低耦合性:测试代码与Appium driver代码解耦,便于维护。
3. PO设计原则
- 用公共方法代表页面提供的功能
- 不要暴露页面元素到外部
- 一般不在方法内加断言
- 方法应该返回其他PO对象
- 不需要封装页面内所有元素
- 同样的行为不同的结果可以封装成不同的方法
4. PO定义与实现
目录的结构:
base:存放一些框架与页面的公共方法
po:存放所有的页面,这里就是被测对象相关的被测页面,不需要放全部页面
result:存放相关的自动化测试结果报告
test_case:存放测试用例
5. PO设计模式实现App页面定义与封装
例:公共基类
public class Pobase {
private AndroidDriver driver;
public Pobase(AndroidDriver driver){
System.out.println("初始化Pobase");
this.driver = driver;
System.out.println(driver.toString());
PageFactory.initElements(new AppiumFieldDecorator(driver),this);
System.out.println("初始化Pobase完成");
}
public void waitAllActivateElement( int timeOut) {
try{
driver.manage().timeouts().implicitlyWait(timeOut, TimeUnit.SECONDS);
}catch (NoSuchElementException e){
System.out.println("元素未找到: ");
}catch (TimeoutException e){
System.out.println("元素未找到: ");
}
}
}
某一页面 类:声明操作元素,及其对应方法,具体的测试流程则交由其他类实现
public class LoginPage extends Pobase{
@FindBy(id="com.formssi.oa:id/phoneNumberEditText")
private WebElement username;
@FindBy(id="com.formssi.oa:id/passwordEditText")
private WebElement password;
@FindBy(id="com.formssi.oa:id/ck_agreement")
private WebElement ck_agreement;
@FindBy(id="com.formssi.oa:id/loginButton")
private WebElement loginButton;
public LoginPage(AndroidDriver driver)
{
super(driver);
waitAllActivateElement(10);
}
public void login(String username,String password){
this.username.sendKeys(username);
this.password.sendKeys(password);
this.ck_agreement.click();
this.loginButton.click();
}
public WebElement getUsername() {
return username;
}
public void setUsername(WebElement username) {
this.username = username;
}
public WebElement getPassword() {
return password;
}
public void setPassword(WebElement password) {
this.password = password;
}
第七章. 测试报告简介
1. ExtentReports测试报告框架简介
ExtentReports 是一个用于自动化测试的 reporting 工具,它能够生成详细的测试报告,用于跟踪测试执行的结果和性能。这个工具可以与多种测试框架(如 Selenium、JUnit、TestNG 等)集成,为用户提供了一个强大的报告生成解决方案。
主要特性: 😄
- 丰富的断言报告:ExtentReports 可以生成丰富的断言报告,包括失败截图、日志堆栈等。
- HTML 报告:生成的报告是 HTML 格式的,易于阅读和导航,可以直接在浏览器中打开。
- 定制报告:用户可以自定义报告的标题、主题和报告内容,以适应不同的需求。
- 分页报告:长测试运行可以生成分页报告,便于用户阅读。
- 集成日志:可以集成日志文件,方便查看测试执行过程中的详细日志。
- 测试环境和数据:报告可以包含测试环境和数据的信息,便于分析和调试。
- 图表和统计:报告中包含图表和统计信息,如测试通过率、执行时间等。
- 高级过滤和搜索:用户可以按照测试结果、类别、时间等条件进行过滤和搜索。
2. 测试报告组成
ExtentReports 是一个自动化测试框架的 reporting 工具,它能够为自动化测试提供丰富的报告。一个典型的 ExtentReports 测试报告通常由以下几个部分组成:
- 报告封面(Report Header):
- 报告的标题和子标题,通常包含测试套件的名称和版本信息。
- 生成报告的日期和时间。
- 测试环境的概述,如浏览器类型、操作系统、测试工具版本等。
- 测试概览(Test Summary):
- 测试套件的概览,包括测试用例总数、通过数、失败数、跳过数和测试时长。
- 测试结果的饼图或条形图,以视觉方式展示测试结果的分布。
- 测试用例详情(Test Cases):
- 每个测试用例的详细信息,包括测试名称、执行时间、测试结果(通过、失败、跳过)、所属类别和测试描述。
- 测试用例的详细日志、错误堆栈和失败截图(如果有)。
- 测试步骤(Test Steps):
- 测试用例中的每个步骤,包括步骤描述、执行时间、状态(通过、失败、跳过)和步骤摘要。
- 步骤摘要通常包括步骤执行的时间、是否抛出异常、异常信息等。
- 日志和环境信息(Logs and Environment Details):
- 测试执行过程中的日志信息,包括INFO、DEBUG、ERROR等日志级别。
- 测试环境的具体信息,如数据库连接、服务器配置等。
- 附件(Attachments):
- 如果测试中有附件(如截图、日志文件等),这些附件会作为报告的一部分包含在内。
- 分页(Pagination):
- 对于包含大量测试用例的报告,ExtentReports 支持分页显示,以便于用户浏览和查找。
- 过滤和搜索(Filtering and Searching):
- 报告提供过滤和搜索功能,允许用户根据测试结果、测试类别、测试名称等条件筛选和查找测试。
- 图表和统计(Charts and Statistics):
- 报告通常包含图表,如测试结果分布图、测试执行趋势图等,以及关键统计数据,如测试通过率、平均测试时长等。
ExtentReports 的报告是高度可定制的,你可以根据需要自定义报告的样式、结构和内容。通过使用 ExtentReports,测试人员可以生成结构化、易于理解和分析的测试报告,这些报告对于测试结果的回顾、问题和缺陷的追踪非常有帮助。
第八章. Jenkins可持续集成开发
1. Jenkins简介
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成(CI)工具(服务器),用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件项目可以持续集成。主要用于自动化各种任务,包括构建、测试和部署软件等
Jenkins可以构建一个自动化的持续集成环境可以使用它来“自动化”编译打包分发部署应用、自动化测试,它兼容ant、maven、gradle等多种第三方椅建工具,同时与svn、git能无缝集成,也支持直接与知名源代码托管网站,如github、bitbucket直接集成。
2. Jenkins运行及原理
Jenkins的工作原理是先将源代码从qithub中接贝一份到本地,然后根据设置的脚本进行build。整个系统的关键就是build脚本,用来告诉jenkins在一次集成中需要执行的任务。
代码提交至qit,通过Jenkins进行配置,将git代码提交至服务器,并且发布到指定的文件夹,Jenkins有触发器配置,每次有新代码提交,git上代码会自动上传到服务器并自动更新发布。如下图所示:
- Jenkins 是一个开源的持续集成(CI)服务器,它的核心原理和运行机制文字说明如下:
① 服务器启动: Jenkins 作为一个服务在后台运行,可以作为独立的 Java 应用程序启动,也可以通过容器化技术如 Docker 运行。它监听一个端口,等待客户端(通常是开发者)的请求。
② 项目配置: 在 Jenkins 界面中,管理员或具有权限的用户可以创建或导入项目,配置项目的构建触发器、构建步骤、环境变量、依赖项等。这些配置通常保存在 Jenkins 的 XML 配置文件中。
③ 构建触发:
源代码管理集成:Jenkins 可以与 Git、SVN 等版本控制系统集成,监听代码仓库的变更,如提交、合并请求等,当检测到变更时自动触发构建。
定时触发:可以设置定时任务,让 Jenkins 在特定时间间隔自动触发构建。
手动触发:用户可以在 Jenkins 界面手动触发构建。
④ 构建过程:
代码获取:Jenkins 从配置的代码仓库拉取最新的代码到其工作空间(workspace)。
构建环境准备:根据项目配置,Jenkins 可能会安装必要的构建工具、依赖库,设置环境变量。
构建执行:执行构建脚本,如 Maven 的 mvn clean install 或 Gradle 的 ./gradlew build,这通常包括编译、测试、打包等步骤。
插件集成:Jenkins 支持丰富的插件,可以扩展功能,如代码质量检查、代码覆盖率报告等。
⑤ 测试和质量检查:
运行测试:如果项目配置了测试,Jenkins 将运行 TestNG、JUnit 等测试框架的测试用例。
生成报告:测试结果被收集并生成报告,如 TestNG 的 HTML 报告。
⑥ 结果通知:
构建结果:构建成功或失败的信息会显示在 Jenkins 界面,也可以通过邮件、Slack、HipChat 等方式通知相关人员。
持续集成管道:如果使用了 Jenkinsfile 或 Pipeline,可以定义更复杂的流水线,包括部署、回滚等操作。
⑦ 工作区管理: Jenkins 为每个项目维护一个独立的工作区,用于存放源代码、构建输出和临时文件。在构建完成后,工作区会被清理,以准备下一次构建。
⑧ 可扩展性: Jenkins 通过插件系统可以扩展其功能,支持更多的版本控制工具、构建工具、测试框架等。总之,Jenkins 的运行机制是监听源代码仓库的变动,自动或按计划拉取代码,执行构建和测试,生成报告,并提供对构建结果的可视化展示和通知。这种自动化流程极大地提高了开发团队的生产力和软件的质量。
3. 第一个Jenkins项目管理{#todo}
第九章. 实操
1. 搭建Appium自动化测试环境
2. 使用Appium连接AOS手机查找元素
3. 编写一个登录功能的自动化测试Demo
- @BeforeSuite ==>
suitSetup()
- Appium服务器环境 remote
- suit测试套件名称 emailable-report-aos
- 测试环境: uatf
TestNG监听器TestListenerAdapter onStart()
onStart//对应测试套件suit中test标签
创建ExtentReport测试报告对象 - @BeforeTest ==>
testSetup()
- remoteHubURL “http://127.0.0.1/wdhub”
- capaPara 机型参数参数+App包信息
- dataFileDir 测试数据目标
- lucklinko01_Login->datapool/uatf/home/luckLink001_Login.json
- @BeforeClass ==>
classSetup()
无操作
TestNG监听器TestListenerAdapter onTestStart()- 加载数据,获取测试数据
dataProvider = "fetchData_JSON”
dataProviderClass=JSONDataProvider.class - 创建driver
- 加载数据,获取测试数据
- @BeforeMethod ==>
methodSetUp()
启动测试App,即
driver.start();
测试用例Case - @Test ==>
luckLinko01_Login()
执行自动化测试业务,完成目标场景测试
TestNG监听器TestListenerAdapter- 如果测试成功 onTestSuccess()
- 如果测试失败 onTestFailure()
- 如果测试忽略 onTestSkipped()
- @AfterMethodmethod ==>
methodTeardown()
Report.getinstance().flush();
//刷新ExtentReport对象,把测试内容写入文件html
CreateMobileDriver.getlnstance().closeApp();
//关闭App
生成ExtentReport格式测试报告 - @AfterClass ==>
classTeardown()
无操作
- @AfterTest ==>
testTeardown()
CreateMobileDriveVgetnstaneeo.closeDriver;
//driver与> > appium服务器断开 - @AfterSuit ==>
SuitTeardown()
Report.getinstance().flush();
//刷新ExtentReport对象,把测试内容写入文件html
根据程序设计框架,当前实现了:
- ① 真机参数通过配置文件读取
- ② PO设计模式管理登录页面(还未使用泛型)
- ③ 乐联的自动化登录测试
这里展示部分核心代码:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Test_Appium4_Project</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>9.2.2</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version> <!-- 或者使用最新稳定版本 -->
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
</project>
phoneParam.json
[
{
"name": "小米MAX3",
"udid": "",
"platformName": "Android",
"platformVersion": "10.0",
"deviceName": "35b5caf4",
"appPackage": "com.formssi.oa",
"appActivity": ".com.formssi.work.main.SplashActivity",
"orientation": "portrait",
"autoAcceptAlerts": true,
"autoGrantPermissions": true,
"serviceUrl": "http://localhost:4723/wd/hub"
},
{
"name": "Google Pixel 3a",
"udid": "ghi789jkl012",
"platformName": "Android",
"platformVersion": "11.0",
"deviceName": "Google Pixel 3a",
"appPackage": "com.example.yourapp",
"appActivity": ".YourMainActivity",
"orientation": "portrait",
"autoAcceptAlerts": true,
"autoGrantPermissions": true
}
]
org/example/appium/Appium.java
package org.example.appium;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.options.BaseOptions;
import org.testng.log4testng.Logger;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class Appium {
private AndroidDriver driver;
private BaseOptions options;
Logger logger = Logger.getLogger(Appium.class);
public Appium(){
}
public AndroidDriver setUp() {
logger.info("准备启动AndroidDriver...");
// 读取 JSON 文件并解析设备列表
List<Map<String, Object>> devices = readDeviceConfig("src/main/resources/phoneParam.json");
String targetDeviceName = "小米MAX3"; // 更改为要使用的设备名称
Map<String, Object> deviceConfig = getDeviceConfigByName(devices, targetDeviceName);
// 使用解析到的设备配置初始化 Appium 参数
initSession(deviceConfig);
try {
driver = new AndroidDriver(new URL(deviceConfig.get("serviceUrl").toString()), options);
logger.info("启动AndroidDriver成功!!!");
} catch (MalformedURLException e) {
logger.error("启动AndroidDriver失败:" + e.getMessage());
}
return driver;
}
private void initSession(Map<String, Object> deviceConfig) {
logger.info("初始化appium启动参数...");
// logger.info( "使用设备配置:" + deviceConfig);
options = new BaseOptions()
.amend("platformName", deviceConfig.get("platformName"))
.amend("Appium:platformVersion", deviceConfig.get("platformVersion"))
.amend("Appium:noReset", "false")
.amend("Appium:fullReset", "true")
.amend("Appium:deviceName", deviceConfig.get("deviceName"))
// .amend("Appium:ensureWebviewsHavePages", true)
// .amend("Appium:nativeWebScreenshot", true)
// .amend("Appium:newCommandTimeout", 3600)
// .amend("Appium:connectHardwareKeyboard", true)
.amend("Appium:appPackage", deviceConfig.get("appPackage"))
.amend("Appium:appActivity", deviceConfig.get("appActivity"));
}
private List<Map<String, Object>> readDeviceConfig(String filePath) {
Gson gson = new Gson();
Type listType = new TypeToken<List<Map<String, Object>>>(){}.getType();
try (FileReader reader = new FileReader(filePath)) {
return gson.fromJson(reader, listType);
} catch (IOException e) {
logger.error("读取设备配置文件失败:" + e.getMessage());
}
return null;
}
private Map<String, Object> getDeviceConfigByName(List<Map<String, Object>> devices, String deviceName) {
for (Map<String, Object> device : devices) {
if (device.get("name").equals(deviceName)) {
return device;
}
}
logger.error("找不到名为 '" + deviceName + "' 的设备配置");
return null;
}
public void tearDown() {
int time = 8;
for (int i = time; i >= 1; i--) {
logger.info(i + "秒后将关闭Appium...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.info("Appium已关闭!!!");
driver.quit();
}
}
src/test/java/testcase/Test2.java
package testcase;
import io.appium.java_client.android.AndroidDriver;
import org.example.PO.PIndex;
import org.example.PO.PLogon;
import org.example.appium.Appium;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import org.testng.log4testng.Logger;
import java.util.concurrent.TimeUnit;
public class Test2 {
Logger logger = Logger.getLogger(Test2.class);
Appium appium = new Appium();
AndroidDriver driver;
@BeforeTest
public void setUp() {
this.driver = appium.setUp();
}
@Test
public void test() {
logger.info("测试开始了");
driver.manage().timeouts().implicitlyWait(9, TimeUnit.SECONDS);
PLogon pLogon = new PLogon(driver);
pLogon.login("zhoujun", "Zj19961021");
PIndex pIndex = new PIndex(driver);
pIndex.getContact().click();
// driver.manage().timeouts().implicitlyWait(9, TimeUnit.SECONDS);
// File screenShot = driver.getScreenshotAs(OutputType.FILE);
// try {
// FileUtils.copyFile(screenShot, new File("src/test/file/"+System.currentTimeMillis()+".png"));
// System.out.println("截图成功");
// }catch (IOException e){e.printStackTrace();}
logger.info("测试结束了");
}
@AfterTest
public void tearDown() {
appium.tearDown();
}
}
4. 环境及脚本报错的解决方案
在使用TestNG和Appium进行自动化测试时,会遇到多种环境和脚本相关的错误。以下是一些常见的问题及其解决方案:
- Appium服务器未启动或无法连接
解决方案:确保Appium服务器已经启动,可以通过命令行或GUI工具检查其状态。如果使用远程服务器,检查网络连接和服务器配置。
- 设备未正确连接或模拟器未启动
解决方案:确保目标设备(物理设备或模拟器)已开启且处于可连接状态,检查USB调试(如果适用)。
- 找不到Activity
解决方案:检查appActivity配置是否正确,与应用实际的启动Activity一致。可参考:
adb查看App的appPackage和appActivity_adb查看appactivity-CSDN博客
- 权限问题
解决方案:确保测试应用有正确的权限,如读写存储、访问网络等。
- 设备版本不兼容
解决方案:检查Appium驱动程序版本是否与测试设备的Android版本兼容,升级或降级驱动。
- Session冲突
解决方案:关闭所有现有的Appium会话,确保每次测试开始时只有一个活动的会话。
- 测试脚本语法错误
解决方案:使用IDE的语法检查功能,或者在运行时查看错误日志定位语法问题。
- 测试数据问题
解决方案:检查数据提供器的配置,确保数据文件路径正确,数据格式符合预期。
- 环境变量问题
解决方案:确保系统环境变量(如ANDROID_HOME, JAVA_HOME等)设置正确。
- Selenium或WebDriver版本不兼容
解决方案 :检查Selenium版本与Appium和WebDriver版本的兼容性,可能需要升级或降级。
对于每个错误,最重要的是查看详细的错误日志,因为这些日志通常会包含导致问题的具体原因和可能的解决方案。此外,查阅官方文档、社区论坛和Stack Overflow等资源也是解决问题的好方法。
5. 搭建Jenkins可持续集成管理平台
5.1 下载
下载地址:Jenkins 的安装和设置 https://www.jenkins.io/zh/download/
这里下载的是war包,如下图所示:
5.2 安装与配置
5.2.1 通过下载jenkins.msi安装:
由于公司环境不支持管理员权限,此路不通~
5.2.2 通过下载war包安装:
参考:【Linux】war包安装部署Jenkins_jenkins war包部署-CSDN博客
在war包目录下,cmd界面:
C:\Users\Public\Downloads\jdk-21.0.3\bin\java -jar jenkins.war
其实是java -jar jenkins.war
,因为需要指定jdk,所以手动指定了。
至此Jenkins服务启动成功
5.2.3 初步配置Jenkins:
默认端口是8080,在浏览器打开http://localhost:8080/
会显示一个解锁Jenkins的页面,如下图:
这个密码按照提示的文件路径就可以找到!
密码输入成功之后,点击继续,将会进入插件下载选项,按照默认下载就行了,下图是下载界面:
这些插件大概需要下载15min。
插件下载完成之后会出现创建管理员的页面,页面中所有文本框内容都是需要填写的,如下图所示:
当前密码是formssi.com
点击【保存完并成】,至此Jenkins初步配置完成。
6. 使用Jenkins实现自动化测试管理
6.1 新建Jenkins项目
这里以新建一个Jenkins的Freestyle项目为例,涉及以下几个详细步骤(界面和选项可能会随着Jenkins版本的更新而有所变化,但基本流程大致相同)。以下是实践的步骤:
- 登录Jenkins并访问项目列表
打开Jenkins的Web界面,这里访问的是 http://localhost:8080/。
登录到Jenkins,使用管理员或具有相应权限的账户。- 创建新项目
在Jenkins首页,点击左侧菜单栏的“新建任务”(New Item)或者“新建”(Create new jobs)按钮。
在弹出的窗口中,为项目起一个名称,比如“MyProject”,然后从项目选项中选择【Freestyle project】。点击“确定”(OK)继续。如图所示:
6.2 配置Jenkins项目
进入项目配置页面后,将看到多个配置选项卡,以根据需要填写或选择以下内容:
6.2.1 General Tab(常规)
常规项下包含许多选项,详解如下:
- 描述(Description)
允许为项目添加一个简短的文本描述,方便其他用户了解项目的目的或功能。
- Discard old builds(丢弃旧的构建)
通过设置保留构建的数量或时间,Jenkins可以自动清理不再需要的旧构建,以节省存储空间。
- GitHub 项目
如果你的项目托管在GitHub上,这里可以关联GitHub仓库,以便启用GitHub Webhook触发器或其他GitHub集成功能。
- This project is parameterized(此项目参数化)
如果勾选,可以为构建添加参数,允许用户在触发构建时输入值,以动态改变构建行为。
- Throttle builds
限制项目的并发构建数量,避免过度占用系统资源。
- 在必要的时候并发构建
如果启用了Throttle builds,此选项允许在满足某些条件时并发构建,例如当所有限制都未达到时。
【高级】
这里提供了更深入的配置选项,如构建触发器的详细设置、构建环境的自定义等。
- 静默期(Quiet Period)
设置一个等待时间,确保连续的多次构建请求合并为一次,避免频繁触发不必要的构建。
- 重试次数
如果构建失败,可以设置重试次数,Jenkins会尝试重新构建一定次数。
- 该项目的上游项目正在构建时阻止该项目构建
如果勾选,当依赖的上游项目正在构建时,Jenkins将阻止当前项目构建,以防止资源冲突。
该项目的下游项目正在构建时阻止该项目构建
相反,如果下游项目正在构建,也可以阻止当前项目构建,确保构建顺序。
- 使用自定义的工作空间
可以指定项目的专用工作目录,而不是默认的工作空间。该选项的配置可以实现Jenkins管理本地项目,如下图:
- 显示名称
为项目设置一个自定义的显示名称,不同于项目名称,通常用于UI显示。
- 保留构建的依赖日志
选择是否保存构建的依赖日志,这可以帮助追踪构建过程中依赖项的变化。
这些设置可以根据项目的实际需求进行调整,以优化构建流程和资源管理。
6.2.2 Source Code Management(源码管理)
源码管理部分用于配置如何从远程仓库获取源代码,主要功能是根据代码存放位置,选择相应的源码管理工具(如Git、Subversion等),如图:
下面是针对Git的详细解释:
Repositories 这个部分用于配置项目的Git仓库。
- Repository URL
输入Git仓库的URL,例如:https://github.com/username/repo.git。
- Credentials
选择或输入用于连接Git仓库的凭证。这可能是用户名和密码,或者SSH密钥,取决于仓库配置。如果没有配置,可以选择“无”。
高级 提供更多高级配置选项。
Add Repository 如果项目涉及多个Git仓库,可以在这里添加额外的仓库。
- Branches to build
定义要构建的Git分支。默认的*/master表示构建主分支。可以指定其他分支,例如feature/*,或者使用通配符来匹配多个分支。
- 源码库浏览器
选择一个源码浏览器插件,如GitWeb或GitHub,用于在构建页面上链接到源码仓库的特定提交。
- Additional Behaviours
这里可以添加额外的行为,如:
Clean before checkout:在检出代码之前清理工作空间。
Prune stale remote tracking branches:删除不再存在的远程跟踪分支。
Use shallow clone:只克隆最近的提交,以减少克隆时间。
Advanced Submodule Options:如果项目包含Git子模块,可以配置子模块的行为。
这些配置可以根据你的项目需求进行调整,以确保Jenkins能够正确地从Git仓库获取代码并进行构建。
6.2.3 Build Triggers(构建触发器)
在Jenkins的“配置”的构建触发器部分,定义了何时自动开始构建项目。以下是各种触发器的详细解释:
- 触发远程构建 (例如,使用脚本)
允许通过远程API调用来触发构建。这通常用于集成测试、部署脚本或其他自动化流程中。生成一个安全的令牌或密码,然后在外部脚本中使用它来触发Jenkins构建。
- Build after other projects are built
当其他特定的Jenkins项目构建完成后,自动触发当前项目的构建。这对于构建链和依赖关系管理很有用。
- Build periodically
设置一个定时计划(通常使用cron表达式),让Jenkins按照设定的时间间隔自动构建项目。例如,H 2 * * *表示每天凌晨2点进行构建。
- GitHub hook trigger for GITScm polling
如果项目托管在GitHub上,这个选项会配置一个webhook,当GitHub仓库中有新的推送时,Jenkins会自动检测并触发构建。无需手动设置SCM轮询。
- Poll SCM
定期检查源代码管理仓库(如Git、SVN等)是否有变动。使用cron表达式设置检查频率。例如,H/5 * * * *表示每5分钟检查一次。当检测到新的提交时,Jenkins会启动构建。
这些触发器可以根据项目的需求进行组合,以实现灵活的自动化构建策略。例如,可以设置定时构建和GitHub webhook,确保代码提交时和固定时间点都会触发构建。
6.2.4 Build Environment(构建环境)
构建环境指的是用于执行构建任务的物理或虚拟环境,包括操作系统、开发工具、构建工具(如 Maven 或 Gradle)、依赖库、版本控制系统、持续集成/持续部署(CI/CD)工具等。这些环境保证了软件项目的构建、测试和部署的一致性。
- Delete workspace before build starts:
这个选项指示 Jenkins 在每次构建开始前清理工作空间,删除上一次构建留下的文件和目录。这有助于避免旧文件或缓存影响新构建的结果,确保每次构建都是从干净的状态开始。
- Use secret text(s) or file(s):
Jenkins 提供了管理敏感信息(如密码、API密钥)的安全方式。你可以创建“秘密文本”或“秘密文件”,这些信息在构建过程中可以安全地引用,而不直接暴露在构建脚本或配置中。这增加了安全性,防止密码和其他敏感数据泄露。
- Add timestamps to the Console Output:
这个选项会在控制台输出中添加时间戳,使得日志更容易阅读和分析。每个打印的行都会附带当前时间,这对于追踪构建过程中的时间线和定位问题非常有用。
- Inspect build log for published build scans:
如果你使用了像 SonarQube 这样的代码质量扫描工具,这个选项可能意味着在构建过程中检查并发布这些扫描结果。这可以帮助你识别潜在的代码质量问题、漏洞和代码复杂性指标。
- Terminate a build if it’s stuck:
这个功能用于自动终止那些长时间无响应的构建。设置一个超时时间,当构建超过这个时间没有输出或进展时,Jenkins 会停止该构建,防止资源浪费。
- With Ant:
Ant 是一个基于 XML 的构建工具,常用于Java项目。在 Jenkins 中,你可以配置使用 Ant 来执行构建脚本,这些脚本定义了构建、测试和打包的步骤。虽然现在 Maven 更为流行,但一些老项目可能仍然使用 Ant。
在配置 Jenkins 作业时,这些选项提供了灵活性,可以根据项目需求定制构建过程,确保高效、安全且可靠的自动化构建流程。
6.2.5 Build Steps(构建步骤)
添加构建步骤,这取决于项目需求。对于Java项目,常见的构建步骤包括:
Invoke top-level Maven targets:
调用Maven目标,如clean install来编译和打包项目,如图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Execute shell 或 Execute Windows batch command:
执行脚本命令,用于自定义构建逻辑或部署脚本。
其他选项不再说明,使用时再学习扩展~
6.2.6 Post-build Actions(构建后操作)
配置构建成功或失败后的动作,例如发送邮件通知、归档构建产物、发布到私有仓库等。
当下邮箱配置还没弄明白,后续补充~
所有的参数配置完成后,就可以点击保存,然后去构建项目了。
6.3 保存配置并构建
完成配置后,滚动到底部点击“保存”(Save)或“应用”(Apply)按钮。保存后,回到项目主页,点击“立即构建”(Build Now)按钮开始首次构建。如下图所示:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
6.4 查看构建结果和日志
构建开始后,可以在项目页面的“构建历史”中查看构建状态。
点击具体的构建编号,可以查看详细的构建日志,以及任何构建步骤的输出或错误信息。
最终,将Appium自动化测试项目使用Jenkins实现自动化测试,如下如:
6.5 故障排查与优化
- 如果构建失败,根据日志提示进行问题排查,可能需要返回配置页面调整设置。
- 根据项目需求持续优化构建流程,比如添加更多自动化测试、优化构建速度等。
- 以上就是创建和配置Jenkins Freestyle项目的基本流程。根据实际情况,可能需要调整或添加更多的配置细节。
7. 实操总结
7.1 收获
- 在本次自动化测试的实践中,我成功地搭建了 Appium 自动化测试环境,并使用 Appium 连接 AOS 手机进行了元素查找和操作。同时,我还编写了一个乐联登录功能的自动化测试 Demo,并实现了真机参数通过配置文件读取和 PageObject 设计模式管理登录页面的功能。此外,还搭建了 Jenkins 可持续集成管理平台,并使用 Jenkins 实现了自动化测试管理。
- 在实践过程中,我遇到了一些问题和挑战,例如 Appium 服务器未启动或无法连接、设备未正确连接或模拟器未启动、找不到 Activity 等。通过查看详细的错误日志和查阅官方文档、社区论坛等资源,我成功解决了这些问题。
- 虽然本次实践还有一些遗留的问题和知识需要继续学习和实践,例如PO泛型、工厂模式去生产PO、报告及监听、Jenkins发邮件等,但通过本次实践,我对自动化测试有了更深入的了解,并掌握了一些基本的自动化测试技能。
- 在后续的学习和实践过程中,我将继续完善和优化自动化测试流程,并尝试解决遗留的问题,并且完善文档,给自己和讲师一个有效的反馈。我相信,通过不断的学习和实践,我能够更好地掌握自动化测试的相关技能,并为项目提供更高质量的自动化测试支持。
第十章. 扩展{#todo3}
通知、归档构建产物、发布到私有仓库等。
当下邮箱配置还没弄明白,后续补充~
所有的参数配置完成后,就可以点击保存,然后去构建项目了。