At present, I haven't involved in webui automatic testing in my work, but in order to improve my technology, it's not harmful to learn more. There's no more nonsense. At present, the mainstream webui testing framework should still be selenium. Considering maintainability, expansibility and reusability, we use PO mode to write our script, This document also mainly integrates Selenium+PO mode + Pytest+Allure. Let's get to the point. Note: Github address is attached at the end of the article
Technical premise: basic knowledge of python, selenium and pytest
1. Project structure directory:
2. Introduction to Po mode
PO mode features:
- Easy to maintain
- High reusability
- The script is easy to read and understand
PO mode elements:
1. In PO mode, it is abstractly encapsulated into a BasePage class, which should have an attribute that only implements webdriver instances
2. Each pag inherits the BasePage, manages the elements in the page through the driver, and encapsulates the operations in the page into methods one by one
3. TestCase relies on the page class to implement the corresponding test steps
3. BasePage page encapsulation
import logging import os import time from datetime import datetime from time import sleep from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait from selenium.common.exceptions import TimeoutException, NoSuchElementException from Utils.myLog import MyLog """ This class encapsulates all operations, and all pages inherit this class """ class BasePage(object): def __init__(self, driver): self.logger = MyLog().getLog() self.driver = driver # Wait for elements to be visible def wait_eleVisible(self, loc, timeout=30, poll_frequency=0.5, model=None): """ :param loc:Element location expression;Tuple type,Expression(Element location type,Element positioning method) :param timeout:Upper limit of waiting :param poll_frequency:Polling frequency :param model:When waiting fails,Screenshot operation,Function labels to be expressed in picture files :return:None """ self.logger.info(f'wait for"{model}"element,Positioning mode:{loc}') try: start = datetime.now() WebDriverWait(self.driver, timeout, poll_frequency).until(EC.visibility_of_element_located(loc)) end = datetime.now() self.logger.info(f'wait for"{model}"duration:{end - start}') except TimeoutException: self.logger.exception(f'wait for"{model}"Element failed,Positioning mode:{loc}') # screenshot self.save_webImgs(f"Wait element[{model}]An exception occurred") raise # Wait element not visible def wait_eleNoVisible(self, loc, timeout=30, poll_frequency=0.5, model=None): """ :param loc:Element location expression;Tuple type,Expression(Element location type,Element positioning method) :param timeout:Upper limit of waiting :param poll_frequency:Polling frequency :param model:When waiting fails,Screenshot operation,Function labels to be expressed in picture files :return:None """ logging.info(f'wait for"{model}"disappear,Element positioning:{loc}') try: start = datetime.now() WebDriverWait(self.driver, timeout, poll_frequency).until_not(EC.visibility_of_element_located(loc)) end = datetime.now() self.logger.info(f'wait for"{model}"duration:{end - start}') except TimeoutException: self.logger.exception(f'wait for"{model}"Element failed,Positioning mode:{loc}') # screenshot self.save_webImgs(f"Wait element[{model}]Vanishing anomaly") raise # Find an element element def find_element(self, loc, model=None): self.logger.info(f'lookup"{model}"Element, element positioning:{loc}') try: return self.driver.find_element(*loc) except NoSuchElementException: self.logger.exception(f'lookup"{model}"Element failed,Positioning mode:{loc}') # screenshot self.save_webImgs(f"Find element[{model}]abnormal") raise # Find element elements def find_elements(self, loc, model=None): self.logger.info(f'lookup"{model}"Element set:{loc}') try: return self.driver.find_elements(*loc) except NoSuchElementException: self.logger.exception(f'lookup"{model}"Element set failed,Positioning mode:{loc}') # screenshot self.save_webImgs(f"Find element set[{model}]abnormal") raise # Input operation def input_text(self, loc, text, model=None): # Find element ele = self.find_element(loc, model) # Input operation self.logger.info(f'stay"{model}"input"{text}",Element positioning:{loc}') try: ele.send_keys(text) except: self.logger.exception(f'"{model}"Input operation failed!') # screenshot self.save_webImgs(f"[{model}]Input exception") raise # Clear operation def clean_inputText(self, loc, model=None): ele = self.find_element(loc, model) # Clear operation self.logger.info(f'eliminate"{model}",Element positioning:{loc}') try: ele.clear() except: self.logger.exception(f'"{model}"Clear operation failed') # screenshot self.save_webImgs(f"[{model}]Clear exception") raise # Click operation def click_element(self, loc, model=None): # Find the element first and click ele = self.find_element(loc, model) # Click operation self.logger.info(f'click"{model}",Element positioning:{loc}') try: ele.click() except: self.logger.exception(f'"{model}"Click failed') # screenshot self.save_webImgs(f"[{model}]Click exception") raise # Get text content def get_text(self, loc, model=None): # Find the element first and get the text content ele = self.find_element(loc, model) # Get text self.logger.info(f'obtain"{model}"Element text content, element positioning:{loc}') try: text = ele.text self.logger.info(f'obtain"{model}"The element text content is"{text}",Element positioning:{loc}') return text except: self.logger.exception(f'obtain"{model}"Element text content failed,Element positioning:{loc}') # screenshot self.save_webImgs(f"obtain[{model}]Text content exception") raise # Get property value def get_element_attribute(self, loc, name, model=None): # First find the element to get the attribute value ele = self.find_element(loc, model) # Get element attribute value self.logger.info(f'obtain"{model}"Element attribute:{loc}') try: ele_attribute = ele.get_attribute(name) self.logger.info(f'obtain"{model}"element"{name}"Property set is"{ele_attribute}",Element positioning:{loc}') return ele_attribute except: self.logger.exception(f'obtain"{model}"element"{name}"Property failed,Element positioning:{loc}') # screenshot self.save_webImgs(f"obtain[{model}]Attribute exception") raise # iframe switching def switch_iframe(self, frame_refer, timeout=30, poll_frequency=0.5, model=None): # Wait for iframe to exist self.logger.info('iframe Switching operation:') try: # Switch = = index\name\id\WebElement WebDriverWait(self.driver, timeout, poll_frequency).until( EC.frame_to_be_available_and_switch_to_it(frame_refer)) sleep(0.5) self.logger.info('Switch successful') except: self.logger.exception('iframe Switching failed!!!') # screenshot self.save_webImgs(f"iframe Switching exception") raise # Window switching = if switching to a new window, new If it is to return to the default window, default def switch_window(self, name, cur_handles=None, timeout=20, poll_frequency=0.5, model=None): """ Get before calling window_handles :param name: new Represents the latest open window. default Represents the first window. Other values are represented as the of the window handles :param cur_handles: :param timeout:Upper limit of waiting :param poll_frequency:Polling frequency :param model:When waiting fails,Screenshot operation,Function labels to be expressed in picture files :return: """ try: if name == 'new': if cur_handles is not None: self.logger.info('Switch to the latest open window') WebDriverWait(self.driver, timeout, poll_frequency).until(EC.new_window_is_opened(cur_handles)) window_handles = self.driver.window_handles self.driver.swich_to.window(window_handles[-1]) else: self.logger.exception('Switching failed,There is no information to switch windows!!!') self.save_webImgs("Switching failed_There is no information to switch windows") raise elif name == 'default': self.logger.info('Switch to default page') self.driver.switch_to.default() else: self.logger.info('Switch to handles Window of') self.driver.swich_to.window(name) except: self.logger.exception('Failed to switch windows!!!') # screenshot self.save_webImgs("Switching failed_There is no information to switch windows") raise # screenshot def save_webImgs(self, model=None): # filepath = refers to the image saving directory / model (page function name)_ The current time is seconds png # Screenshot saving directory # Splice log folder, if it does not exist, will be created automatically cur_path = os.path.dirname(os.path.realpath(__file__)) now_date = time.strftime('%Y-%m-%d', time.localtime(time.time())) screenshot_path = os.path.join(os.path.dirname(cur_path), f'Screenshots\\{now_date}') if not os.path.exists(screenshot_path): os.mkdir(screenshot_path) # current time dateNow = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())) # route filePath = '{}\\{}_{}.png'.format(screenshot_path, model, dateNow) try: self.driver.save_screenshot(filePath) self.logger.info(f"Screen capture successful,Picture path is{filePath}") except: self.logger.exception('Screen capture failed!') # sign out def get_driver(self): return self.driver
4. Page inheritance BasPage
from Common.basePage import BasePage from selenium.webdriver.common.by import By from time import sleep class BaiduIndex(BasePage): """ Page element """ # Baidu link home page baidu_index_url = "https://www.baidu.com" # Search box search_input = (By.ID, "kw") # "Baidu click" button box search_button = (By.ID, "su") # Query operation def search_key(self, search_key): self.logger.info("[===Search operation===]") # Wait for the user name text box element to appear self.wait_eleVisible(self.search_input, model='Search box') # Input content self.input_text(self.search_input, "Atri", model="Search box") # Clear text box contents self.clean_inputText(self.search_input, model='Search box') # enter one user name self.input_text(self.search_input, text=search_key, model='Search box') # Wait for the search button to appear self.wait_eleVisible(self.search_button, model='"use Baidu Search"Search button') # Click the search button self.click_element(self.search_button, model='"use Baidu Search"Search button') # Wait for the interface to load after searching self.driver.implicitly_wait(10) sleep(3)
5. pytest+allure write test cases
Note: please refer to: https://www.cnblogs.com/huny/p/13752406.html
import os import time import pytest import allure from time import sleep from selenium import webdriver from PageObject.baiduIndex import BaiduIndex driver = webdriver.Chrome() baidu_index = BaiduIndex(driver) @pytest.fixture(scope="class") def init(): # Open the browser to access the login page baidu_index.logger.info("\nWebDriver Initializing...") driver.get(baidu_index.baidu_index_url) baidu_index.logger.info(f"Open link: {baidu_index.baidu_index_url}...") # window maximizing driver.maximize_window() # Implicit waiting driver.implicitly_wait(10) baidu_index.logger.info("WebDriver Initialization complete!") yield driver.quit() baidu_index.logger.info("WebDriver Exit successfully...") @allure.feature("Baidu search") class TestBaiduSearch: @allure.story("Search for specified keywords") @pytest.mark.baidu_search @pytest.mark.parametrize("key_word", [ "ha-ha", "ha-ha", ], ) def test_search(self, init, key_word): # @pytest. mark. Parameterize parameterization baidu_index.search_key(key_word) web_title = driver.title assert "ha-ha_Baidu search" == web_title
6. Generate Allure test report
Github address: https://github.com/Zimo6/Selenium_Demo
The following is the supporting materials. For friends who do [software testing], it should be the most comprehensive and complete war preparation warehouse. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you!
Finally, it can be in the official account: programmer Hao! Get a 216 page interview document of Software Test Engineer for free. And the corresponding video learning tutorials for free!, It includes basic knowledge, Linux essentials, Shell, Internet program principles, Mysql database, special topics of packet capture tools, interface test tools, test advanced Python programming, Web automation test, APP automation test, interface automation test, advanced continuous integration of test, test architecture, development test framework, performance test, security test, etc.
If my blog is helpful to you and you like my blog content, please click "like", "comment" and "collect" for three times! Friends who like software testing can join our testing technology exchange group: 779450660 (there are various software testing resources and technical discussions)