前言:
之前通過重寫unittest的初始化方法加入設備參數進行並發,實現了基於unittest的appium多設備並發,但是考慮到unittest的框架實在過於簡陋,也不方便后期的Jenkins的持續集成,所以想換一個框架來使用。
那么通過調研,pyhon+pytest+allure 這套框架很不錯,pytest是一個單元測試框架,他可以集成很多插件,包括出錯重試,參數化,等。在此特別是基於他的allure插件,能夠和Jenkins完美兼容,生成美觀強大的測試報告。
改造思路:
pytest框架和unittest框架明顯不同,通過命令行啟動,讀取響應目錄下的test開頭的文件,進行執行用例。
而unittest卻是通過將用例加載到TestSuite中,運行隨測試集來執行用例
所以這邊多進程就要換一種思路進行了。
基於pytest的結構和運行方式,那么思路如下:
運行方式:
1. pytest目錄下會先加載conftest.py運行。
2. 該目錄下加載test開頭的py文件
3. 加載文件中Test開頭的類
4. 加載Test類下test開頭的方法
5. 通過命令行pytest.main([1, 2 ,3])帶入1 2 3參數進行運行
解決思路:
1. 通過命令行把不同設備的參數傳遞給conftest.py
2. conftest中,使用傳遞過來的設備參數,連接設備到appium,並生成driver對象
3. 在各自的測試類和測試方法中,調用driver對象,進行測試操作
4. 生成allure測試報告
實現:
1. 通過命令行傳遞參數:
run中的設備池:
def devices_Pool(): devices_list = [] for i in range(0, len(getDevices())): _initApp = {} _initCaps = {} _initApp["devices"] = getDevices()[i] _initCaps["deviceName"] = getDevices()[i] _initCaps["platformVersion"] = getPhoneInfo(devices=_initCaps["deviceName"])["release"] _initCaps["platformName"] = "Android" _initApp["port"] = str(random.randint(4700, 4900)) _initApp["bport"] = str(random.randint(4700, 4900)) _initApp["systemPort"] = str(random.randint(4700, 4900)) _initCaps["automationName"] = "UiAutomator2" _initCaps["appPackage"] = 'cn.vsx.vc' _initCaps["appActivity"] = '.activity.RegistActivity' _initApp["Caps"] = _initCaps devices_list.append(_initApp) print(len(getDevices())) print(len(devices_list)) return devices_list
run中,多進程調用啟動命令行,並傳遞參數:
def runnerPool(device_list): getdevice = getDevices() with ProcessPoolExecutor(len(getdevice)) as pool: pool.map(runPytest, device_list) def runPytest(device): print(f"cmdopt is {device}") report = f"report-{device['Caps']['deviceName']}".split(":", 1)[0] try: os.system(f"del /s /q E:\\appium-pytest\\{report}") time.sleep(1) os.system(f"rd /s /q E:\\appium-pytest\\{report}") time.sleep(1) print(f"{report} report has deleted") except: print("no directory existed") finally: print(f"pool run device is {device['devices']}") pytest.main(["../TestCases/", f"--cmdopt={device}", "--alluredir", f"../{report}/xml"]) time.sleep(1) os.system(f"allure generate ../{report}/xml -o ../{report}/html")
conftest文件中,獲取命令行傳遞過來的參數:
def pytest_addoption(parser): parser.addoption("--cmdopt", action="store", default="device", help="None") @pytest.fixture(scope="session") def cmdopt(request): return request.config.getoption("--cmdopt")
conftest中通過傳遞的參數,生成連接對象:
@pytest.fixture(scope="session") def connectDevice(cmdopt): device = eval(cmdopt) device_caps = {} device_caps["platformVersion"] = getPhoneInfo(device["Caps"]["deviceName"])["release"] device_caps["platformName"] = "Android" device_caps["automationName"] = "UiAutomator2" device_caps["deviceName"] = device["Caps"]['deviceName'] device_caps["udid"] = device["Caps"]['deviceName'] device_caps["appPackage"] = "cn.vsx.vc" device_caps["appActivity"] = ".activity.RegistActivity" device_caps["noReset"] = True device_caps["noSign"] = True device_caps["unicodeKeyboard"] = True device_caps["resetKeyboard"] = True device_caps["systemPort"] = int(device["systemPort"]) remote = "http://127.0.0.1:" + str(device["port"]) + "/wd/hub" print(f"wo shi pytest {device_caps}") driver = webdriver.Remote(remote, device_caps) return driver
測試用例中,使用對象來進行操作:
class Test_groupCall(): @allure.feature("group_call") @allure.story("login") def test001_login(self, connectDevice): '''登入選擇單位''' WebDriverWait(connectDevice, 10).until( lambda x: x.find_element_by_xpath( "//android.widget.TextView[contains(@text, '選擇單位')]").is_displayed()) # 驗證等待10秒超時 x = connectDevice.get_window_size()['width'] # 獲取當前屏幕寬 y = connectDevice.get_window_size()['height'] # 獲取當前屏幕高 a, b = 170 / 768, 790 / 1184 # 選擇單位222系數 connectDevice.find_element_by_xpath("//android.widget.TextView[contains(@text, '選擇單位')]").click()
最后:
多設備連接時,一定要注意給每個desired_caps中加入每個設備自己的systemPort,否則會連接不上多設備,至此改造成功,最后生成的報告也讓人滿意: