日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

一种很轻松的Excel关键字方式进行网页Web自动化测试(Java+Selenium+TestNG+Excel)

發布時間:2023/12/16 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一种很轻松的Excel关键字方式进行网页Web自动化测试(Java+Selenium+TestNG+Excel) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言


最初目的,為了在整體測試平臺框架下,測試人員可便捷維護用例,無需啟動服務、編譯程序,提供一套Excel填寫測試用例并能快速執行的解決方案。后續擴展了不同類型的測試用例,本文則對Web類型的測試用例Excel方式執行測試邏輯進行介紹。
開發過程中,本想著網上CV大法搞下來修修改改,結果發現沒一個清晰完整的分享,于是,結合自身理解,以及測試框架設計,形成了目前的一個版本,同時分享給測試小伙伴,希望能對各位測試伙伴能有所幫助。

其他說明

  • 開發語言Java,采用SpringBoot開發框架(版本2.1.18.RELEASE)(文章尾部會貼出各依賴版本POM文件),測試底層調用Selenium框架方法,以及測試注解引用TestNG測試框架,本文所述功能僅為整體測試框架中一個小功能點,所以分享并沒有全部貼出(不是作者不想,作者自研,完全有權限分享,只是內容太多,沒整理好,就暫時不發出來)
  • 所有代碼均有注釋,代碼基本不再細致說明,以及一些額外相關的代碼沒貼出來(主要是放這里顯得雜亂,有機會單寫文章說明整體架構邏輯及源碼,有興趣留言交流)
  • 本文主要從作者主觀邏輯、代碼進行介紹,可能部分方法、部分邏輯并未介紹到位,作者寫文純屬鍛煉下文檔(好久沒寫文檔了),不完善的地方請各位玩家理解,歡迎留言咨詢,后續看源碼通過什么形式發出來
  • 文章作者:@隨心自然fqc , 轉載時,請注明來源,注明作者,這是對文章作者的尊重,也是對知識的尊重。

整體圖解


先看看整體邏輯,看完整體代碼邏輯后,建議再回頭看看這張圖 ><

主要內容

1、測試用例調用方式
import cn.nhdc.cloud.modules.casemanage.model.vo.TestCaseVo; import cn.nhdc.cloud.testscripts.listener.ExtentTestNGIReporterListener; import cn.nhdc.cloud.testscripts.testcase.base.WebTestBase; import org.testng.annotations.Listeners; import org.testng.annotations.Optional; import org.testng.annotations.Test;import java.util.List;/*** Web類型 Excel執行方式 自動化測試demo <br>* Method 1 : {@link #testExcelExcute} 正向邏輯執行 <br>* Method 2 : {@link #testExcelInvokeExcute} 反射邏輯執行 <br>** ps: 以上2種方式 原理不同 但執行效果類似 具體選擇哪種取決于調用者意愿** @author Fan QingChuan*/ @Test(description = "Excel方式自動化測試示例-正向邏輯&反射執行邏輯") @Listeners(value = ExtentTestNGIReporterListener.class) public class ExcelWebTestDemo extends WebTestBase {@Test(description = "正向邏輯: 操作方法封裝在 WebTestBase 中, 測試用例中可直接調用 解析Excel -> caseSteps 根據遍歷操作編碼switch執行各項操作")void testExcelExcute(@Optional("C:\\Users\\allen\\Desktop\\自動化測試_Web類型測試用例_2022_07_07_185853_437.xlsx") String fileName,@Optional("testcase")String sheetName,@Optional("null")Integer headerRowNumber) {List<TestCaseVo> caseVoList = analysisExcelUiCase(fileName, sheetName, headerRowNumber);excuteExcelUiTest(caseVoList);}@Test(description = "反射邏輯: 操作封裝在 WebCommon 通過 WebTestBase 中反射獲取BasePage.class 并實例化 再根據操作編碼+分類枚舉 獲取對應方法 并反射(invoke)執行測試")void testExcelInvokeExcute(@Optional("C:\\Users\\allen\\Desktop\\自動化測試_Web類型測試用例_2022_07_21_134019_690.xlsx") String fileName,@Optional("testcase")String sheetName,@Optional("null")Integer headerRowNumber)throws IllegalAccessException, InstantiationException {List<TestCaseVo> caseVoList = analysisExcelUiCase(fileName, sheetName, headerRowNumber);invokeExcelUiTest(caseVoList);}}
2、測試用例對象Vo
import cn.nhdc.cloud.modules.casemanage.service.Add; import cn.nhdc.cloud.modules.casemanage.service.Update; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data;import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.List;/*** * @author Fan QingChuan*/ @Data public class TestCaseVo {@NotNull(message = "caseId(用例ID)不能為空", groups = {Update.class})private Long caseId;/*** 測試集ID*/private Long suiteId;/*** 團隊code */@NotBlank(message = "teamCode 團隊code不能為空", groups = {Add.class, Update.class})private String teamCode;/*** 測試用例編碼*/@NotBlank(message = "caseCode 用例編號不能為空", groups = {Add.class, Update.class})private String caseCode;/*** 測試用例類型 1-接口測試 2-Web測試 3-安卓APP測試 4-IOS APP測試 9-其他混合測試*/@NotNull(message = "type 測試用例類型不能為空", groups = {Add.class, Update.class})private Integer type;/*** 用例順序號*/private Integer sort;/*** 用例描述*/private String description;/*** 測試人員姓名*/@NotBlank(message = "tester 測試人員姓名不能為空", groups = {Add.class, Update.class})private String tester;/*** 測試步驟List*/@Validprivate List<UiCaseStepVo> caseSteps;@Datapublic static class UiCaseStepVo{@NotNull(message = "caseId 測試用例ID不能為空",groups = {Update.class})private Long caseId;@NotBlank(message = "caseCode 測試用例編碼不能為空",groups = {Update.class})private String caseCode;@NotNull(message = "stepId 步驟ID不能為空",groups = {Update.class})private Long stepId;/*** 步驟順序號*/@NotNull(message = "sort 步驟序號不能為空",groups = {Add.class, Update.class})private Integer sort;/*** 步驟描述*/@ExcelProperty("測試步驟描述")private String description;/*** 關鍵字(操作)編碼*/@NotBlank(message = "actionKeyword 關鍵字(操作)編碼不能為空",groups = {Add.class, Update.class})private String actionKeyword;/*** 元素定位方式(類型)*/private String elementLocateType;/*** 元素定位信息*/private String elementLocateValue;/*** 輸入值*/private String parameter;} }
  • 如代碼所示,將測試用例各字段都考慮進去,如需擴展API測試,可在放進該對象
  • 字段上各校驗注解,是用例管理CRUD接口中需要的,與本次分享毫無關系(例如 @NotNull(message = “caseId(用例ID)不能為空”, groups = {Update.class})),如有測試小白,可忽略,作者懶癌犯了,就沒刪除
3、解析Excel >> 測試用例

這個步驟就是將Excel填寫好的測試用例,解析成我們剛才看到的一個個TestCaseVo對象,即List<TestCaseVo> , 用的EasyExcel工具解析

import cn.hutool.core.util.ObjectUtil; import cn.nhdc.cloud.common.utils.HttpUtils; import cn.nhdc.cloud.modules.casemanage.model.vo.ExcelCaseStepVo; import cn.nhdc.cloud.modules.casemanage.model.vo.TestCaseVo; import cn.nhdc.cloud.testscripts.config.ReportLog; import cn.nhdc.cloud.testscripts.testcase.base.TestBase; import com.alibaba.excel.EasyExcel; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.xiaoleilu.hutool.bean.BeanUtil;import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors;/*** * @author Fan QingChuan*/ public interface TestCommon{/*** 解析Excel UI測試用例 <br>* 解析邏輯: 按測試用例編碼(caseCode)進行分組,將所有步驟封裝到對應用例編碼下 <br>* 與測試用例-測試步驟數據結構對應, {@link TestCaseVo.UiCaseStepVo} 一對多關系** @param filePath 文件絕對路徑* @param sheetName 需解析的sheetName 默認Sheet1* @param headerRowNumber 解析case的標題行數 默認1行* @return a List of {@link TestCaseVo}*/default List<TestCaseVo> analysisExcelUiCase(String filePath, String sheetName, Integer headerRowNumber){List<ExcelCaseStepVo> caseStepVos = EasyExcel.read(filePath).head(ExcelCaseStepVo.class).sheet(sheetName != null ? sheetName : "Sheet1").headRowNumber(ObjectUtil.isNotEmpty(headerRowNumber) && headerRowNumber > 0 ? headerRowNumber : 1).doReadSync();reportLog.info(" ======== >> 解析到[{}]條數據",caseStepVos.size());List<String> caseCodeList = caseStepVos.stream().map(ExcelCaseStepVo::getCaseCode).distinct().collect(Collectors.toList());List<TestCaseVo> caseVoList = new ArrayList<>(caseCodeList.size());reportLog.info(" ======== >> 重新組裝成[{}]條測試用例",caseCodeList.size());caseCodeList.forEach(caseCode -> {TestCaseVo testCaseVo = new TestCaseVo();testCaseVo.setCaseCode(caseCode);List<TestCaseVo.UiCaseStepVo> caseSteps = caseStepVos.stream().filter(o -> caseCode.equals(o.getCaseCode())).map(excelCaseStepVo -> {TestCaseVo.UiCaseStepVo uiCaseStepVo = new TestCaseVo.UiCaseStepVo();BeanUtil.copyProperties(excelCaseStepVo,uiCaseStepVo);return uiCaseStepVo;}).collect(Collectors.toList());testCaseVo.setCaseSteps(caseSteps);caseVoList.add(testCaseVo);});return caseVoList;}default void threadSleep(String seconds) {if (seconds == null || Long.parseLong(seconds) < 1 ) {throw new IllegalArgumentException("threadSleep 未設置休眠秒數 即輸入值(秒值)不能為空 且 值需大于0");}long second = Long.parseLong(seconds);try {Thread.sleep(second * 1000);} catch (InterruptedException e) {e.printStackTrace();}} }
4、執行測試邏輯

我們得到了List<TestCaseVo> caseVoList,下一步自然就是執行測試了,如果你是測試小白,那作者希望你能仔細讀讀以下代碼。
其中關鍵點:

  • 區分 excuteExcelUiTest 與 invokeExcelUiTest 的調用邏輯區別
  • 不同關鍵字操作方法區別(以及對應在測試活動中的區別)

通過此類 你將收獲到一絲絲靈感,方便你進行自己的創作

import cn.hutool.core.util.ObjectUtil; import cn.nhdc.cloud.modules.casemanage.model.vo.TestCaseVo; import cn.nhdc.cloud.testscripts.config.ReportLog; import cn.nhdc.cloud.testscripts.enums.WebActionTypeEnum; import cn.nhdc.cloud.testscripts.page.base.BasePage; import cn.nhdc.cloud.testscripts.testcase.base.common.WebCommon; import org.openqa.selenium.WebDriver; import org.springframework.web.multipart.MultipartFile; import org.testng.Assert; import org.testng.ITestResult; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite;import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors;/*** * @author Fan QingChuan*/ public abstract class WebTestBase implements WebCommon {public static final int TIME_OUT = 10;public static final int SLEEP_TIME = 300;private static final ReportLog reportLog = new ReportLog(WebTestBase.class);protected WebDriver baseWebDriver;/*** 執行測試(Excel UI自動化測試-正常邏輯)<br>* 正常邏輯: 按測試用例循環遍歷測試,在一個測試用例中,先將用例下所有測試步驟按步驟序號進行排序(升序), <br>* 再按升序進行測試, 每個步驟按操作編碼進行識別調用對應操作方法進行測試,* 調用方式: switch(actionType) case ...actionMethod 將所有操作類型分為4種調用方式 詳情請看{@link WebActionTypeEnum}** @param caseVoList 解析后的測試用例List {@link TestCaseVo}*/public void excuteExcelUiTest(List<TestCaseVo> caseVoList) {caseVoList.forEach(caseVo -> {List<TestCaseVo.UiCaseStepVo> caseSteps = caseVo.getCaseSteps().stream().sorted(Comparator.comparingInt(TestCaseVo.UiCaseStepVo::getSort)).collect(Collectors.toList());caseSteps.forEach(step -> {reportLog.info("測試用例編號[{}] 步驟序號:[{}],測試步驟描述 [{}],操作編碼:[{}],元素定位方式[{}],定位值[{}],輸入參數值[{}]",step.getCaseCode(),step.getSort(),step.getDescription(),step.getActionKeyword(),step.getElementLocateType(),step.getElementLocateValue(),step.getParameter());WebActionTypeEnum webActionTypeEnum = WebActionTypeEnum.getTargetType(step.getActionKeyword());Assert.assertNotNull(webActionTypeEnum,"操作編碼不正確或不存在!");switch (webActionTypeEnum) {//type-0case ClosePage:closePage();break;//此處省略類似代碼(其他操作關鍵字方式)//type-1case OpenUrl:if (step.getParameter() == null) {throw new IllegalArgumentException("openUrl 輸入值(網址)不能為空 步驟序號:" +step.getSort() +"用例編號:"+caseVo.getCaseCode());}openUrl(step.getParameter());break;//此處省略類似代碼(其他操作關鍵字方式)//type-2case Click:if (step.getElementLocateType() == null || step.getElementLocateValue() == null) {throw new IllegalArgumentException("click 元素定位不能為空 步驟序號:" +step.getSort() +"用例編號:"+caseVo.getCaseCode());}click(step.getElementLocateType(), step.getElementLocateValue());break;//此處省略類似代碼(其他操作關鍵字方式)//type-3case ClickCustomWait:if (step.getElementLocateType() == null || step.getElementLocateValue() == null || step.getParameter() == null) {throw new IllegalArgumentException("clickCustomWait 元素定位與輸入值均不能為空 步驟序號:" +step.getSort() +"用例編號:"+caseVo.getCaseCode());}String[] parameters = step.getParameter().split(":");if (parameters.length > 2) {throw new IllegalArgumentException("clickCustomWait 等待配置輸入值不合法 格式:超時時間(單位秒):檢查頻率(單位毫秒) 例如:3:500 表示等待3秒,每500毫秒檢查一次 步驟序號:" +step.getSort() +"用例編號:"+caseVo.getCaseCode());}clickCustomWait(Long.valueOf(parameters[0]),Long.valueOf(parameters[1]),step.getElementLocateType(),step.getElementLocateValue());break;//此處省略類似代碼(其他操作關鍵字方式)//type-4case InitBaseWebBrowser:initBaseBrowser(step.getParameter());break;case ThreadSleep:threadSleep(step.getParameter());break;default:throw new IllegalArgumentException("操作編碼不合法! 或 不存在該操作 如有必要請聯系我增加 > <");}});});}protected void closePage() {closePage(baseWebDriver);}protected void openUrl(String url) {openUrl(baseWebDriver,url);}protected void click(String elementLocateType, String elementLocateValue) {click(baseWebDriver,elementLocateType,elementLocateValue);}protected void clickCustomWait(long outTimeSeconds, long sleep, String elementLocateType, String elementLocateValue) {clickCustomWait(baseWebDriver,outTimeSeconds,sleep,elementLocateType,elementLocateValue);}//此處省略類似代碼(其他操作)/*** 初始化基類baseWebDriver* @param browserInfo*/protected void initBaseBrowser(String browserInfo) {//buildWebBrowser生產新的driverbaseWebDriver = buildWebBrowser(browserInfo);}/*** 執行測試(Excel UI自動化測試-反射邏輯)<br>* 反射邏輯: 按測試用例循環遍歷測試,在一個測試用例中,先將用例下所有測試步驟按步驟序號進行排序(升序), <br>* 再按升序進行測試, 每個步驟按操作編碼進行識別反射調用對應操作方法進行測試, <br>* 調用方式: switch(actionType) case ...invoke(actionMethod) 將所有操作類型分為4種調用方式 詳情請看{@link WebActionTypeEnum} <br>* 與正向邏輯的區別在于 反射邏輯需實例化對應基類- {@link BasePage} 并在每個測試步驟傳入上下文的 WebDriver,* 另需注意buildWebBrowser(初始化打開瀏覽器)操作屬特殊情況,不能傳入WebDriver,因為在正常情況下,buildWebBrowser前WebDriver為null沒初始化,* 當然就不存在上文WebDriver** @param caseVoList 解析后的測試用例List {@link TestCaseVo}* @throws InstantiationException* @throws IllegalAccessException*/public void invokeExcelUiTest(List<TestCaseVo> caseVoList) throws InstantiationException, IllegalAccessException {Class<?> clazz = null;try {clazz = Class.forName("cn.nhdc.cloud.testscripts.page.base.BasePage");} catch (ClassNotFoundException e) {e.printStackTrace();}assert clazz != null;BasePage instance = (BasePage) clazz.newInstance();Class<?> finalClazz = clazz;caseVoList.forEach(caseVo -> {List<TestCaseVo.UiCaseStepVo> caseSteps = caseVo.getCaseSteps().stream().sorted(Comparator.comparingInt(TestCaseVo.UiCaseStepVo::getSort)).collect(Collectors.toList());caseSteps.forEach(step -> {try {reportLog.info("測試用例編號[{}] 步驟序號:[{}],測試步驟描述 [{}],操作編碼:[{}],元素定位方式[{}],定位值[{}],輸入參數值[{}]",step.getCaseCode(),step.getSort(),step.getDescription(),step.getActionKeyword(),step.getElementLocateType(),step.getElementLocateValue(),step.getParameter());if (step.getActionKeyword().equals(WebActionTypeEnum.InitBaseWebBrowser.getActionKeyword())) {Method method = finalClazz.getMethod(step.getActionKeyword(),String.class);this.baseWebDriver = (WebDriver) method.invoke(instance,step.getParameter());}else {invokeMethod(finalClazz, instance, this.baseWebDriver, step.getActionKeyword(), step.getElementLocateType(), step.getElementLocateValue(), step.getParameter());}} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}});});}private void invokeMethod(Class<?> clazz, Object instance, WebDriver driver, String actionKeyword, String elementLocateType, String elementLocateValue, String parameter) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {WebActionTypeEnum webActionTypeEnum = WebActionTypeEnum.getTargetType(actionKeyword);if (ObjectUtil.isNull(webActionTypeEnum)) {throw new IllegalStateException("操作編碼 actionKeyword 不合法");}Method method;switch (webActionTypeEnum.getType()) {case 0:method = clazz.getMethod(actionKeyword,WebDriver.class);method.invoke(instance,driver);break;case 1:method = clazz.getMethod(actionKeyword,WebDriver.class,String.class);method.invoke(instance,driver,parameter);break;case 2:method = clazz.getMethod(actionKeyword,WebDriver.class,String.class,String.class);method.invoke(instance,driver,elementLocateType,elementLocateValue);break;case 3:method = clazz.getMethod(actionKeyword,WebDriver.class,String.class,String.class,String.class);method.invoke(instance,driver,elementLocateType,elementLocateValue,parameter);break;case 4:method = clazz.getMethod(actionKeyword,String.class);method.invoke(instance,parameter);break;default:break;}} }

也許你會好奇 ->

* 反射邏輯-invokeExcelUiTest中,調用反射執行invokeMethod(...),這些具體操作方法在哪里? * 正向邏輯-excuteExcelUiTest中,調用WebTestBase.closePage() ,而closePage()中又調用了個closePage(baseWebDriver)? WTF?

答案就是

  • 對于公共操作,作者都將其進行了集中抽取并分層管理,類似最開始圖解中 WebCommon -> TestCommon ,包括作者沒貼出來的AndroidCommon -> AppCommon -> WebCommon -> TestCommon
    每層通用的均各自層級實現,調用者只需要implements 對應抽象類即可調用。

PS: 是不是你就懂了前文 TestCommon中為什么有個方法 threadSleep(String seconds)了,除了可提供給用戶Excel操作關鍵字,也可以視為所有測試活動都可以用這個方法(線程休眠)

那么回到主題,WebCommon長什么樣子呢? 請往下繼續

import cn.nhdc.cloud.common.utils.DateUtils; import cn.nhdc.cloud.testscripts.config.Assertion; import cn.nhdc.cloud.testscripts.config.WebDriverFactory; import cn.nhdc.cloud.testscripts.testcase.base.WebTestBase; import cn.nhdc.common.util.CollectionUtils; import io.appium.java_client.AppiumBy; import org.apache.commons.io.FileUtils; import org.openqa.selenium.*; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.ITestResult;import java.io.File; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List;/*** * @author Fan QingChuan*/ public interface WebCommon extends TestCommon {int WEB_COMMON_TIME_OUT = WebTestBase.TIME_OUT;int WEB_COMMON_SLEEP_TIME = WebTestBase.SLEEP_TIME;default void closePage(WebDriver driver) {if (driver != null) {driver.close();}else {throw new RuntimeException("不存在運行的瀏覽器 closePage失敗! ");}}default void openUrl(WebDriver driver,String url) {driver.get(url);}default void click(WebDriver driver,String elementLocateType, String elementLocateValue) {WebElement element_click = new WebDriverWait(driver, Duration.ofSeconds(WEB_COMMON_TIME_OUT), Duration.ofMillis(WEB_COMMON_SLEEP_TIME)).until(ExpectedConditions.elementToBeClickable(getBy(elementLocateType, elementLocateValue)));element_click.click();}/*** 點擊元素-自定義等待** @param driver* @param outTimeSeconds 最大等待時間 單位秒* @param sleep 檢查頻率 單位 毫秒* @param elementLocateType 定位方式* @param elementLocateValue 定位信息*/default void clickCustomWait(WebDriver driver,long outTimeSeconds, long sleep, String elementLocateType, String elementLocateValue) {WebElement element_click = new WebDriverWait(driver, Duration.ofSeconds(outTimeSeconds), Duration.ofMillis(sleep)).until(ExpectedConditions.elementToBeClickable(getBy(elementLocateType, elementLocateValue)));element_click.click();}//此處省略類似代碼(其他操作關鍵字具體行為)/*** 初始化瀏覽器并返回給調用者* @param browserInfo* @return*/default WebDriver buildWebBrowser(String browserInfo) {if (browserInfo == null) {throw new IllegalArgumentException("buildBrowser 輸入值不能為空 格式 瀏覽器名稱:版本號 例如 chrome:102");}String[] browser = browserInfo.split(":");if (browser.length > 2) {throw new IllegalArgumentException("buildBrowser 瀏覽器名稱及版本號格式不合法 示例: 類型:版本號 chrome:102");}//實例化新的driverreturn WebDriverFactory.initDriver(browser[0],browser[1]);}

也許聰明的你已經看出來了,所謂關鍵字操作,被作者分成了5類:

* 類型-0 無需定位 無需輸入參數 * 類型-1 無需定位 需輸入參數 * 類型-2 需定位 無需輸入參數 * 類型-3 需定位 需輸入參數 * 類型-4 特殊類型

需要特別說明的,類型-4中 有一個特殊的操作 -> 初始化瀏覽器(WebActionTypeEnum.InitBaseWebBrowser -> buildWebBrowser)
它的特別之處體現在反射方法invokeExcelUiTest中,我們來看

if (step.getActionKeyword().equals(WebActionTypeEnum.InitBaseWebBrowser.getActionKeyword())) {Method method = finalClazz.getMethod(step.getActionKeyword(),String.class);this.baseWebDriver = (WebDriver) method.invoke(instance,step.getParameter()); //這里反射執行方法后,返回了一個對象,被我們強轉成了(WebDriver),猜猜它有什么用? }

明顯的是,我們Web測試過程中依賴瀏覽器的運行,即第一步必須初始化瀏覽器,在我們代碼中,這個瀏覽器驅動(WebDriver)對象應交由后續其他關鍵字操作使用,即我們后續每一步操作都將該WebDriver傳入方法執行即可:

else {invokeMethod(finalClazz, instance, this.baseWebDriver, step.getActionKeyword(), step.getElementLocateType(), step.getElementLocateValue(), step.getParameter()); }

既然操作被分成了5類,那這5類操作都有哪些呢? 下一步我們來看這5種類型定義把

4、操作類型(關鍵字)枚舉
import lombok.AllArgsConstructor; import lombok.Getter; import org.openqa.selenium.Keys;import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;/*** * @author Fan QingChuan*/@Getter @AllArgsConstructor public enum WebActionTypeEnum {//類型-0 無需定位 無需輸入參數ClosePage("關閉頁面","closePage",0,"關閉當前所在頁面窗口"),CloseBrowser("關閉瀏覽器瀏覽器","closeBrowser",0,"關閉整個瀏覽器(所有頁面)"),PageBack("頁面返回","pageBack",0,"頁面返回"),PageForward("頁面前進","pageForward",0,"頁面前進"),PageRefresh("刷新頁面","pageRefresh",0,"頁面刷新"),//類型-1 無需定位 需輸入參數OpenUrl("打開網絡地址","openUrl",1,"打開網址(輸入值-網址)"),NavigateToUrl("跳轉網絡地址","navigateToUrl",1,"(同一個頁面窗口下)跳轉至網址 (輸入值-網址)"),NavigateToWindows("跳轉窗口至目標窗口","navigateToWindows",1,"跳轉窗口至目標窗口(輸入值-頁面標題)"),Pause("暫停N秒","pause",1,"暫停N秒(輸入值-秒值 支持小數)"),OpenUrlBlank("Blank方式(新開頁面)打開網址","openUrlBlank",1,"Blank方式(新開頁面)打開網址 (輸入值-網址)"),KeyBoard("輸入鍵盤","keyBoard",1,"輸入鍵盤(輸入值 org.openqa.selenium.Keys枚舉中的鍵盤值) 例如"+ Arrays.stream(Keys.values()).map(o -> o.name()).collect(Collectors.toList())),Javascript("執行js腳本","javascript",1,"執行js腳本(輸入值-js腳本)"),//類型-2 需定位 無需輸入參數Click("點擊","click",2,"點擊(元素)"),ClickNegatively("消極等待點擊","clickNegatively",2,"消極等待點擊(元素)"),RightClick("右鍵單擊元素","rightClick",2,"右鍵單擊元素(元素)"),MoveToElement("移動至元素","moveToElement",2,"焦點移動至(元素)"),ClickAndHold("點擊并保持按住","clickAndHold",2,"點擊并保持按下狀態(元素)"),DoubleClick("雙擊","doubleClick",2,"雙擊(元素)"),Release("釋放元素","release",2,"釋放(元素)"),NavigateToFrame("跳轉至Frame","navigateToFrame",2,"跳轉Frame(元素)"),//類型-3 需定位 需輸入參數ClickCustomWait("自定義等待點擊","clickCustomWait",3,"自定義等待點擊(元素)+ 輸入值(時間配置 格式: 等待時間(單位秒):檢查頻率(單毫秒) 例如: 5:300 等待5秒每300毫秒檢查一次)"),ClickNegativelyCustomWait("自定義消極等待點擊","clickNegativelyCustomWait",3,"自定義消極等待點擊(元素)+ 輸入值(時間配置 格式: 等待時間(單位秒):檢查頻率(單毫秒) 例如: 10:500 等待10秒每500毫秒檢查一次)"),InputText("輸入文本","inputText",3,"輸入文本(元素 + 輸入值)"),AssertElementText("斷言類型-元素文本","assertElementText",3,"斷言元素上文本是否符合預期 (元素 + 輸入值-預期文本)"),DragAndDropToPoint("拖拽某元素至目標坐標位置并釋放","dragAndDropToPoint",3,"拖拽某元素至目標坐標位置并釋放 (元素 +輸入值-格式: x坐標:y坐標 例如:300:400 )"),DragAndDropToElement("拖拽某元素至目標元素位置并釋放","dragAndDropToElement",3,"拖拽某元素至目標元素位置并釋放 (元素 + 輸入值 定位方式|定位值 例如: xpath|//*[@id=\"pane-third\"]"),ClickInElementsByText("點擊組元素中文本符合預期的元素","clickInElementsByText",3,"點擊組元素中文本符合預期的元素-(元素)"),//類型-4 特殊類型 不需要WebDriver(其他類型需要WebDriver) 不需定位 傳入需輸入參數ThreadSleep("線程休眠","threadSleep",4,"線程休眠(輸入值 秒值-支持小數)"),InitBaseWebBrowser("實例化(打開)瀏覽器","buildWebBrowser",4,"實例化(打開)瀏覽器(輸入值-瀏覽器類型:版本號)"),;private String name;private String actionKeyword;private Integer type;private String description;public static WebActionTypeEnum getTargetType(String actionKeyword) {for (WebActionTypeEnum value : WebActionTypeEnum.values()) {if (value.getActionKeyword().equalsIgnoreCase(actionKeyword)) {return value;}}return null;}public static List<String> getAllKeyword() {return Arrays.stream(WebActionTypeEnum.values()).map(WebActionTypeEnum::getActionKeyword).collect(Collectors.toList());}public static List<String> getAllDescription() {return Arrays.stream(WebActionTypeEnum.values()).map(WebActionTypeEnum::getDescription).collect(Collectors.toList());} }

解釋下字段吧(雖然有注釋,估計還是不明白為什么要這么多字段)
* name 操作簡稱,主要后續如果需要取名字值時,用這個簡稱比較合適
* actionKeyword 操作關鍵編碼,核心字段,操作方法、用例關鍵字都將與該字段一一完全對應,全局通用
* type 操作類型,根據需要進行定義
* description 描述,當然這個描述,就比較細致了(Excel測試用例中會展示),尤其開發者可以開發一些特殊的連續操作,此時就算看簡稱、操作編碼也不太容易懂,隨即該字段可提供更細致的解釋

5、BasePage? 干嘛用的

接下來,就是大部分測試小伙伴都知道的一種代碼模型 => PO設計模型,作者這里準備了Web頁面的基類,供喜歡PO寫法的小伙伴進行使用,當然此處直接用于反射邏輯中了。

import cn.nhdc.cloud.testscripts.config.ReportLog; import cn.nhdc.cloud.testscripts.testcase.base.common.TestCommon; import cn.nhdc.cloud.testscripts.testcase.base.common.WebCommon; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait;import java.net.URL; import java.time.Duration;/*** * @author Fan QingChuan*/ public class BasePage implements TestCommon, WebCommon {private static final ReportLog reportLog = new ReportLog(BasePage.class);public WebDriver driver;public BasePage() {}public BasePage(WebDriver driver) {this.driver = driver;PageFactory.initElements(new AjaxElementLocatorFactory(driver, WebCommon.WEB_COMMON_TIME_OUT), this);}public BasePage(WebDriver driver, URL url) {this.driver = driver;PageFactory.initElements(new AjaxElementLocatorFactory(driver, WebCommon.WEB_COMMON_TIME_OUT), this);this.driver.get(url.toString());}public BasePage(WebDriver driver, final String title) {this.driver = driver;WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(WebCommon.WEB_COMMON_TIME_OUT));try {boolean flag = wait.until((ExpectedCondition<Boolean>) arg0 -> arg0.getTitle().equals(title));} catch (TimeoutException te) {throw new IllegalStateException("當前不是預期頁面,當前頁面title是:" + driver.getTitle());}PageFactory.initElements(new AjaxElementLocatorFactory(driver, WebCommon.WEB_COMMON_TIME_OUT), this);}}
6、其他相關
  • 本文相關依賴及版本:
<?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><properties><selenium.version>4.1.4</selenium.version><junit5.version>5.9.0-M1</junit5.version><testng.version>7.4.0</testng.version><appium.version>8.0.0</appium.version><slf4j.version>1.7.36</slf4j.version><logback.version>1.2.11</logback.version></properties><dependencies><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>${testng.version}</version></dependency><dependency><groupId>com.xiaoleilu</groupId><artifactId>hutool-all</artifactId><version>3.3.2</version></dependency><dependency><groupId>com.relevantcodes</groupId><artifactId>extentreports</artifactId><version>2.41.2</version></dependency><dependency><groupId>com.vimalselvam</groupId><artifactId>testng-extentsreport</artifactId><version>1.3.1</version></dependency><dependency><groupId>com.aventstack</groupId><artifactId>extentreports</artifactId><version>3.0.6</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.11</version></dependency><!-- https://mvnrepository.com/artifact/io.appium/java-client --><dependency><groupId>io.appium</groupId><artifactId>java-client</artifactId><version>${appium.version}</version></dependency><!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>${selenium.version}</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>${logback.version}</version><scope>runtime</scope></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><scope>runtime</scope></dependency></dependencies></project>
  • 測試用例Excel:
    格式由其他方法進行定義,可自動導出不同測試類型的測試用例,以及參數均通過下拉選值及復制進行快速編寫測試用例,(其他文章單獨說明)

  • Excel關鍵字描述:

  • 運行日志:

  • 測試報告:

總結

以上是生活随笔為你收集整理的一种很轻松的Excel关键字方式进行网页Web自动化测试(Java+Selenium+TestNG+Excel)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。