UI automation framework based on python2 + selenium 3 + pytest4

Environment: Python 2 7.10, selenium3.141.0, pytest4.6.6, pytest-html1.22.0, Windows-7-6.1.7601-SP1

characteristic:

  • Secondary encapsulation of selenium makes it more convenient to write Case.
  • Adopt PO design idea, one page per page Py, in which the elements and operation methods are defined; In TestCase, directly call the operation method encapsulated in the page to operate the page.
  • Start the browser only once in a test to save time and improve efficiency (the one suitable for the company's business is the best).
  • Enhance the content of pytest HTML report and add failure screenshots, use case description columns and operation logs.
  • Command line parameters are supported.
  • Support sending reports by mail.

Directory structure:

  • config
    • config.py: store global variables, various configurations, driver s, etc
  • drive: Browser driver files, such as chromedriver exe
  • file
    • Download: download folder
    • Screenshot: screenshot folder
    • Upload: upload folder
  • page_object: one for each page py to store page objects and operation methods
    • base_page.py: basic page, which encapsulates various operations of selenium
    • hao123_page.py: hao123 page
    • home_page.py: Baidu home page
    • news_page.py: News Home Page
    • search_page.py: search results page
  • report:
    • report.html: report generated by pytest HTML
  • test_case
    • conftest.py: pytest unique file, in which the screenshot of report failure and the column of use case description are added
    • test_home.py: Baidu home page test case
    • test_news.py: test case of NEWS HOMEPAGE
    • test_search.py: search results page test case
  • util: Toolkit
    • log.py: encapsulates the log module
    • mail.py: encapsulates the mail module. To use the send report mail function, you need to set relevant configurations, such as user name and password
  • run.py: as a run entry, it encapsulates the pytest run command; Realize that all test cases share a driver; The operation parameterization is realized (combined with Jenkins); log configuration initialization; You can configure sending report mail.

config.py code implementation:

1. Define a global dictionary to store global variables. key is the variable name and value is the variable value. Parameters can be passed across files and use cases.

2. set_value,get_value is used to save and retrieve global variables respectively.

1 # coding=utf-8
 2 
 3 import os
 4 
 5 
 6 def init():
 7     global _global_dict
 8     _global_dict = {}
 9 
10     # Code root directory
11     root_dir = os.getcwd()
12 
13     # Directory where the program is stored
14     _global_dict['root_path'] = root_dir
15     # Store normal screenshot folder
16     _global_dict['screenshot_path'] = "{}\\file\\screenshot\\".format(root_dir)
17     # Download folder
18     _global_dict['download_path'] = "{}\\file\\download\\".format(root_dir)
19     # Upload folder
20     _global_dict['upload_path'] = "{}\\file\\upload\\".format(root_dir)
21     # Storage report path
22     _global_dict['report_path'] = "{}\\report\\".format(root_dir)
23 
24     # Save driver
25     _global_dict['driver'] = None
26 
27     # Set the web address and home page of the running environment
28     _global_dict['site'] = 'https://www.baidu.com/'
29     # The operating environment is preview by default and can be set to product
30     _global_dict['environment'] = 'preview'
31 
32 
33 def set_value(name, value):
34     """
35     Modify the value of the global variable
36     :param name: Variable name
37     :param value: Variable value
38     """
39     _global_dict[name] = value
40 
41 
42 def get_value(name, def_val='no_value'):
43     """
44     Gets the value of the global variable
45     :param name: Variable name
46     :param def_val: Default variable value
47     :return: Returns the value of a variable when it exists, otherwise'no_value'
48     """
49     try:
50         return _global_dict[name]
51     except KeyError:
52         return def_val

log.py code implementation: encapsulated log module

1 # coding=utf-8
 2 
 3 import logging
 4 import time
 5 import config.config as cf
 6 
 7 
 8 class Logger(object):
 9     """Encapsulated log module"""
10 
11     def __init__(self, logger, cmd_level=logging.DEBUG, file_level=logging.DEBUG):
12         try:
13             self.logger = logging.getLogger(logger)
14             self.logger.setLevel(logging.DEBUG)  # Sets the default level for log output
15             '''pytest Reports can be automatically log Integrate it into the report, and you don't have to set and save it separately
16             # Log output format
17             fmt = logging.Formatter(
18                 '%(asctime)s[%(levelname)s]\t%(message)s')
19             # Log file name
20             curr_time = time.strftime("%Y-%m-%d %H.%M.%S")
21             log_path = cf.get_value('log_path')
22             self.log_file = '{}log{}.txt'.format(log_path, curr_time)
23             # Set console output
24             sh = logging.StreamHandler()
25             sh.setFormatter(fmt)
26             sh.setLevel(cmd_level)
27             # Set file output
28             fh = logging.FileHandler(self.log_file)
29             fh.setFormatter(fmt)
30             fh.setLevel(file_level)
31             # Add log output method
32             self.logger.addHandler(sh)
33             self.logger.addHandler(fh)
34             '''
35         except Exception as e:
36             raise e
37 
38     def debug(self, msg):
39         self.logger.debug(msg)
40 
41     def info(self, msg):
42         self.logger.info(msg)
43 
44     def error(self, msg):
45         self.logger.error(msg)
46 
47     def warning(self, msg):
48         self.logger.warning(msg)

mail.py code implementation: for the encapsulated mail module, the report HTML file will be sent as an attachment. Here, you need to change all the top four variables to your own.

1 # coding=utf-8
 2 
 3 import smtplib
 4 from email.mime.text import MIMEText
 5 from email.mime.multipart import MIMEMultipart
 6 from email.header import Header
 7 import config.config as cf
 8 
 9 
10 def send_mail(sendto):
11     """
12     Send mail
13     :param sendto:Recipient list, such as['22459496@qq.com']
14     """
15     mail_host = 'smtp.sohu.com'  # Mailbox server address
16     username = 'test@sohu.com'  # Mailbox user name
17     password = 'test'  # Mailbox password
18     receivers = sendto  # addressee
19 
20     # Create an instance with attachments
21     message = MIMEMultipart()
22     message['From'] = Header(u'UI automation', 'utf-8')
23     message['subject'] = Header(u'UI Automated test results', 'utf-8')  # Mail title
24     message.attach(MIMEText(u'See Annex for test results', 'plain', 'utf-8'))# Message body
25     # Construction attachment
26     report_root = cf.get_value('report_path')  # Get report path
27     report_file = 'report.html'  # Report file name
28     att1 = MIMEText(open(report_root + report_file, 'rb').read(), 'base64', 'utf-8')
29     att1["Content-Type"] = 'application/octet-stream'
30     att1["Content-Disposition"] = 'attachment; filename={}'.format(report_file)
31     message.attach(att1)
32 
33     try:
34         smtp = smtplib.SMTP()
35         smtp.connect(mail_host, 25)  # 25 is the SMTP port number
36         smtp.login(username, password)
37         smtp.sendmail(username, receivers, message.as_string())
38         print u'Mail sent successfully'
39     except Exception, e:
40         print u'Mail sending failed'
41         raise e

base_page.py code implementation: it encapsulates the common operations of selenium as the base class of all page classes.

1 # coding=utf-8
  2 
  3 from selenium.common.exceptions import TimeoutException
  4 from selenium.webdriver.support.ui import WebDriverWait
  5 from selenium.webdriver.common.keys import Keys
  6 from selenium.webdriver.common.action_chains import ActionChains
  7 import os
  8 import inspect
  9 import config.config as cf
 10 import logging
 11 import time
 12 
 13 log = logging.getLogger('szh.BasePage')
 14 
 15 
 16 class BasePage(object):
 17     def __init__(self):
 18         self.driver = cf.get_value('driver')  # Get driver from global variable
 19 
 20     def split_locator(self, locator):
 21         """
 22         Explode positioning expressions, such as'css,.username',Return after splitting'css selector'And positioning expressions'.username'(class by username Element of)
 23         :param locator: Positioning method+Positioning expression combination string, such as'css,.username'
 24         :return: locator_dict[by], value:Returns the positioning method and positioning expression
 25         """
 26         by = locator.split(',')[0]
 27         value = locator.split(',')[1]
 28         locator_dict = {
 29             'id': 'id',
 30             'name': 'name',
 31             'class': 'class name',
 32             'tag': 'tag name',
 33             'link': 'link text',
 34             'plink': 'partial link text',
 35             'xpath': 'xpath',
 36             'css': 'css selector',
 37         }
 38         if by not in locator_dict.keys():
 39             raise NameError("wrong locator!'id','name','class','tag','link','plink','xpath','css',exp:'id,username'")
 40         return locator_dict[by], value
 41 
 42     def wait_element(self, locator, sec=30):
 43         """
 44         Wait for the element to appear
 45         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
 46         :param sec:Wait seconds
 47         """
 48         by, value = self.split_locator(locator)
 49         try:
 50             WebDriverWait(self.driver, sec, 1).until(lambda x: x.find_element(by=by, value=value),
 51                                                      message='element not found!!!')
 52             log.info(u'Waiting element:%s' % locator)
 53             return True
 54         except TimeoutException:
 55             return False
 56         except Exception, e:
 57             raise e
 58 
 59     def get_element(self, locator, sec=60):
 60         """
 61         Get an element
 62         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
 63         :param sec:Wait seconds
 64         :return: Element can be found and returned element Object, otherwise return False
 65         """
 66         if self.wait_element(locator, sec):
 67             by, value = self.split_locator(locator)
 68             print by, value
 69             try:
 70                 element = self.driver.find_element(by=by, value=value)
 71                 log.info(u'Get element:%s' % locator)
 72                 return element
 73             except Exception, e:
 74                 raise e
 75         else:
 76             return False
 77 
 78     def get_elements(self, locator):
 79         """
 80         Get a set of elements
 81         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
 82         :return: elements
 83         """
 84         by, value = self.split_locator(locator)
 85         try:
 86             elements = WebDriverWait(self.driver, 60, 1).until(lambda x: x.find_elements(by=by, value=value))
 87             log.info(u'Get element list:%s' % locator)
 88             return elements
 89         except Exception, e:
 90             raise e
 91 
 92     def open(self, url):
 93         """
 94         Open URL
 95         :param url: URL connection
 96         """
 97         self.driver.get(url)
 98         log.info(u'Open web address:%s' % url)
 99 
100     def clear(self, locator):
101         """
102         Clear contents of element
103         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
104         """
105         self.get_element(locator).clear()
106         log.info(u'Empty content:%s' % locator)
107 
108     def type(self, locator, text):
109         """
110         Enter content in element
111         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
112         :param text: Input content
113         """
114         self.get_element(locator).send_keys(text)
115         log.info(u'Directional element %s Enter text:%s' % (locator, text))
116 
117     def enter(self, locator):
118         """
119         Press enter on the element
120         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
121         """
122         self.get_element(locator).send_keys(Keys.ENTER)
123         log.info(u'In element %s Press enter up' % locator)
124 
125     def click(self, locator):
126         """
127         Click on the element
128         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
129         """
130         self.get_element(locator).click()
131         log.info(u'Click on the element:%s' % locator)
132 
133     def right_click(self, locator):
134         """
135         Right click the element
136         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
137         """
138         element = self.get_element(locator)
139         ActionChains(self.driver).context_click(element).perform()
140         log.info(u'Right click on the element:%s' % locator)
141 
142     def double_click(self, locator):
143         """
144         Double click the element
145         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
146         """
147         element = self.get_element(locator)
148         ActionChains(self.driver).double_click(element).perform()
149         log.info(u'Double click on the element:%s' % locator)
150 
151     def move_to_element(self, locator):
152         """
153         Point the mouse at the element
154         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
155         """
156         element = self.get_element(locator)
157         ActionChains(self.driver).move_to_element(element).perform()
158         log.info(u'Point to element%s' % locator)
159 
160     def drag_and_drop(self, locator, target_locator):
161         """
162         Drag an element to another element location
163         :param locator: The anchor of the element to drag
164         :param target_locator: Positioning of target location element
165         """
166         element = self.get_element(locator)
167         target_element = self.get_element(target_locator)
168         ActionChains(self.driver).drag_and_drop(element, target_element).perform()
169         log.info(u'Put element %s Drag to element %s' % (locator, target_locator))
170 
171     def drag_and_drop_by_offset(self, locator, xoffset, yoffset):
172         """
173         Drag an element down to the right x,y Offsets
174         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
175         :param xoffset: X offset to move to
176         :param yoffset: Y offset to move to
177         """
178         element = self.get_element(locator)
179         ActionChains(self.driver).drag_and_drop_by_offset(element, xoffset, yoffset).perform()
180         log.info(u'Put element %s Drag to coordinates:%s %s' % (locator, xoffset, yoffset))
181 
182     def click_link(self, text):
183         """
184         Search by part of the link text and click the link
185         :param text: Partial text of the link
186         """
187         self.get_element('plink,' + text).click()
188         log.info(u'Click Connect:%s' % text)
189 
190     def alert_text(self):
191         """
192         return alert text
193         :return: alert text
194         """
195         log.info(u'Get pop-up text:%s' % self.driver.switch_to.alert.text)
196         return self.driver.switch_to.alert.text
197 
198     def alert_accept(self):
199         """
200         alert Point confirmation
201         """
202         self.driver.switch_to.alert.accept()
203         log.info(u'Click the pop-up box to confirm')
204 
205     def alert_dismiss(self):
206         """
207         alert Point cancel
208         """
209         self.driver.switch_to.alert.dismiss()
210         log.info(u'Click the pop-up box to cancel')
211 
212     def get_attribute(self, locator, attribute):
213         """
214         Returns the value of an attribute of an element
215         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
216         :param attribute: Attribute name
217         :return: Attribute value
218         """
219         value = self.get_element(locator).get_attribute(attribute)
220         log.info(u'Get element %s Attribute value for %s Is:%s' % (locator, attribute, value))
221         return value
222 
223     def get_ele_text(self, locator):
224         """
225         Returns the text of the element
226         :param locator: Positioning method+Positioning expression combination string, separated by commas, such as'css,.username'
227         :return: The text of the element
228         """
229         log.info(u'Get element %s The text is:%s' % (locator, self.get_element(locator).text))
230         return self.get_element(locator).text
231 
232     def frame_in(self, locator):
233         """
234         get into frame
235         :param locator: Positioning method+Positioning expression combination string, such as'css,.username'
236         """
237         e = self.get_element(locator)
238         self.driver.switch_to.frame(e)
239         log.info(u'get into frame: %s' % locator)
240 
241     def frame_out(self):
242         """
243         Return to main document
244         """
245         self.driver.switch_to.default_content()
246         log.info(u'sign out frame Return to default document')
247 
248     def open_new_window_by_locator(self, locator):
249         """
250         Click the element to open a new window and switch the handle to the new window
251         :param locator: Positioning method+Positioning expression combination string, such as'css,.username'
252         """
253         self.get_element(locator).click()
254         self.driver.switch_to.window(self.driver.window_handles[-1])
255         log.info(u'Click element %s Open a new window' % locator)
256 
257         # old_handle = self.driver.current_window_handle
258         # self.get_element(locator).click()
259         # all_handles = self.driver.window_handles
260         # for handle in all_handles:
261         #     if handle != old_handle:
262         #         self.driver.switch_to.window(handle)
263 
264     def open_new_window_by_element(self, element):
265         """
266         Click the element to open a new window and switch the handle to the new window
267         :param element: Element object
268         """
269         element.click()
270         self.driver.switch_to.window(self.driver.window_handles[-1])
271         log.info(u'Click the element to open a new window')
272 
273     def js(self, script):
274         """
275         implement JavaScript
276         :param script:js sentence 
277         """
278         self.driver.execute_script(script)
279         log.info(u'implement JS sentence:%s' % script)
280 
281     def scroll_element(self, locator):
282         """
283         Drag the scroll bar to the target element
284         :param locator: Positioning method+Positioning expression combination string, such as'css,.username'
285         """
286         script = "return arguments[0].scrollIntoView();"
287         element = self.get_element(locator)
288         self.driver.execute_script(script, element)
289         log.info(u'Scroll to element:%s' % locator)
290 
291     def scroll_top(self):
292         """
293         Scroll to top
294         """
295         self.js("window.scrollTo(document.body.scrollHeight,0)")
296         log.info(u'Scroll to top')
297 
298     def scroll_bottom(self):
299         """
300         Scroll to bottom
301         """
302         self.js("window.scrollTo(0,document.body.scrollHeight)")
303         log.info(u'Scroll to bottom')
304 
305     def back(self):
306         """
307         Page back
308         """
309         self.driver.back()
310         log.info(u'Page back')
311 
312     def forward(self):
313         """
314         Page forward
315         """
316         self.driver.forward()
317         log.info(u'Page forward')
318 
319     def is_text_on_page(self, text):
320         """
321         Return to page source code
322         :return: Page source code
323         """
324         if text in self.driver.page_source:
325             log.info(u'Judge whether there is text on the page:%s' % text)
326             return True
327         else:
328             log.info(u'Judge that there is no text on the page:%s' % text)
329             return False
330 
331     def refresh(self):
332         """
333         Refresh page
334         """
335         self.driver.refresh()
336         log.info(u'Refresh page')
337 
338     def screenshot(self, info='-'):
339         """
340         screenshot,Name: file name-Method name-notes
341         :param info: Screenshot description
342         """
343         catalog_name = cf.get_value('screenshot_path')  # Get screenshot folder location from global variable
344         if not os.path.exists(catalog_name):
345             os.makedirs(catalog_name)
346         class_object = inspect.getmembers(inspect.stack()[1][0])[-3][1]['self']  # Get the object of the test class
347         classname = str(class_object).split('.')[1].split(' ')[0]  # Get test class name
348         testcase_name = inspect.stack()[1][3]  # Get test method name
349         filepath = catalog_name + classname + "@" + testcase_name + info + ".png"
350         self.driver.get_screenshot_as_file(filepath)
351         log.info(u'Screenshot:%s.png' % info)
352 
353     def close(self):
354         """
355         Close current page
356         """
357         self.driver.close()
358         self.driver.switch_to.window(self.driver.window_handles[0])
359         log.info(u'Close current Tab')
360 
361     def sleep(self, sec):
362         time.sleep(sec)
363         log.info(u'wait for%s second' % sec)

This framework supports all positioning methods of selenium. In order to improve the writing speed, the use method is improved. When defining elements, the method name and method value are a string separated by commas, such as:

  • xpath positioning: i_keyword = 'xpath,//input[@id = "kw"]' # keyword input box
  • id positioning: b_search = 'id,su' # search button
  • Other positioning methods are the same as above and will not be exemplified one by one

When using the type() method in the above code, you enter text in the input box and type(i_keyword, "input content") when calling

Get is called in type()_ The element () method parses the entered positioning expression and waits for the element for a period of time. When the element appears, it will be operated immediately.

In addition, you can see that each basic operation is added to the log. The following figure is the log recorded in the report after the use case runs

search_page.py code implementation: Baidu's search page encapsulated in PO mode inherits the BasePage class above; In each page class, the expressions of each control are defined above, and various operations on the page are encapsulated as methods below. In this way, if control or operation methods are invoked in multiple use cases, updating maintenance in the future will only need to be changed in the page class, and all use cases will be updated.

1 # coding=utf-8
 2 
 3 from page_object.base_page import BasePage
 4 
 5 
 6 class SearchPage(BasePage):
 7     def __init__(self, driver):
 8         self.driver = driver
 9 
10     # i = input box, l = link, im = picture, t = text control, d=div, lab=label
11     # Contain_ Baidu Encyclopedia Search Results
12     l_baike = 'xpath,//a [(. = "Star Story Baidu Encyclopedia")] '
13 
14     # next page
15     b_next_page = 'link,next page>'
16 
17     # previous page
18     b_up_page = 'xpath,//A [(. = "< previous")] '
19 
20     # Click on the Encyclopedia of search results
21     def click_result(self):
22         self.open_new_window_by_locator(self.l_baike)
23         self.sleep(3)
24 
25     # Click next
26     def click_next_page(self):
27         self.click(self.b_next_page)

test_search.py code implementation: the test case of Baidu search page. Here I simply wrote two use cases. The first is to click the first search result after search to open it, and the second is to turn the page of search results. The specific operations in the use case are the operation methods encapsulated in the above page class.

1 # coding=utf-8
 2 
 3 import sys
 4 reload(sys)
 5 sys.setdefaultencoding('utf8')
 6 from page_object.home_page import HomePage
 7 from page_object.search_page import SearchPage
 8 import pytest
 9 import config.config as cf
10 
11 
12 class TestSearch():
13     """
14     pytest:
15     Test files to test_start
16     Test class to Test Start, and cannot have__init__method
17     Test function to test_start
18     Assertion use assert
19     """
20     driver = cf.get_value('driver')  # Get driver from global variable
21     home_page = HomePage(driver)
22     search_page = SearchPage(driver)
23 
24     def test_click_result(self):
25         """Search page-Click the first search result"""
26         try:
27             self.home_page.open_homepage()
28             self.home_page.input_keyword(u'Star Story')  # Enter keywords
29             self.search_page.click_result()  # Click Encyclopedia
30             assert self.home_page.is_text_on_page(u'Opening song of TV series "watching meteor shower together"')  # The validation page opens
31             self.home_page.screenshot(u'Open search results')
32             self.search_page.close()  # Close encyclopedia page
33         except Exception, e:
34             self.home_page.screenshot(u'Failed to open search results')
35             raise e
36 
37     def test_click_next_page(self):
38         """Search page-Search Pagination """
39         try:
40             self.search_page.click_next_page()  # Click next
41             assert self.home_page.wait_element(self.search_page.b_up_page)  # The previous page appears
42             self.search_page.scroll_element(self.search_page.b_up_page)  # Scroll to the previous page
43             self.home_page.screenshot(u'Search Pagination ')
44         except Exception, e:
45             self.home_page.screenshot(u'Search page flipping failed')
46             raise e

conftest.py code implementation:

1.conftest.py is a file that pytest provides data and operation sharing. Its file name is fixed and cannot be modified.

2.conftest. The directory where the PY file is located must exist__ init__.py file.

3. Other files do not need to import conf test Py and pytest examples will be found automatically

4. All test files in the same directory will execute conf test before running Py file

5. Only in confitest Py adds the function of error reporting screenshots. If you need to perform some operations before and after the use case, you can write them here.

1 # coding=utf-8
 2 
 3 import pytest
 4 from py._xmlgen import html
 5 import config.config as cf
 6 import logging
 7 
 8 log = logging.getLogger('szh.conftest')
 9 
10 
11 @pytest.mark.hookwrapper
12 def pytest_runtest_makereport(item):
13     """When the test fails, an automatic screenshot is displayed to html In the report"""
14     pytest_html = item.config.pluginmanager.getplugin('html')
15     outcome = yield
16     report = outcome.get_result()
17     extra = getattr(report, 'extra', [])
18 
19     if report.when == 'call' or report.when == "setup":
20         xfail = hasattr(report, 'wasxfail')
21         if (report.skipped and xfail) or (report.failed and not xfail):
22             file_name = report.nodeid.replace("::", "_") + ".png"
23             driver = cf.get_value('driver')  # Get driver from global variable
24             screen_img = driver.get_screenshot_as_base64()
25             if file_name:
26                 html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
27                        'οnclick="window.open(this.src)" align="right"/></div>' % screen_img
28                 extra.append(pytest_html.extras.html(html))
29         report.extra = extra
30         report.description = str(item.function.__doc__)#.decode('utf-8', 'ignore')  # If it is not decoded and converted to Unicode, an error will be reported when generating HTML
31         # report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")
32 
33 
34 @pytest.mark.optionalhook
35 def pytest_html_results_table_header(cells):
36     cells.insert(1, html.th('Description'))
37     cells.pop()  # Delete links in the last column of the report
38 
39 
40 @pytest.mark.optionalhook
41 def pytest_html_results_table_row(report, cells):
42     cells.insert(1, html.td(report.description))
43     cells.pop()  # Delete links in the last column of the report

run.py code implementation:

1.run.py is used to do some initialization, run tests, and test closure. See the comments in the code for details.

2. Put the initialization of the browser driver here and store the driver in the global variable, so that the browser can run all tests only once. If you want to open and close the browser once for each use case, you can put the method of defining the driver in conf test Py.

3.get_args() is encapsulated command-line parameter parsing, which is convenient to quickly define the running content when integrating Jenkins. At present, only one environment parameter -e is defined. You can set the test environment preview and online environment product. You can add more parameters as needed.

4. Calling method: Python run py -e product

5.main() encapsulates the command line execution mode of pytest, which can also be modified as needed.

1 # coding=utf-8
 2 
 3 import pytest
 4 import config.config as cf
 5 from util.log import Logger
 6 import argparse
 7 from selenium import webdriver
 8 from util.mail import send_mail
 9 
10 
11 def get_args():
12     """Command line parameter parsing"""
13     parser = argparse.ArgumentParser(description=u'Optional parameters:')
14     parser.add_argument('-e', '--environment', choices=['preview', 'product'], default='preview', help=u'testing environment preview,Online environment product')
15     args = parser.parse_args()
16     if args.environment in ('pre', 'preview'):
17         cf.set_value('environment', 'preview')
18         cf.set_value('site', 'http://www.baidu.com/')
19     elif args.environment in ('pro', 'product'):
20         cf.set_value('environment', 'preview')
21         cf.set_value('site', 'https://www.baidu.com/')
22     else:
23         print u"Please enter preview/product"
24         exit()
25 
26 
27 def set_driver():
28     """set up driver"""
29     # Configuring Chrome Driver
30     chrome_options = webdriver.ChromeOptions()
31     chrome_options.add_argument('--start-maximized')  # Browser maximization
32     chrome_options.add_argument('--disable-infobars')  # Don't remind me that chrome is under the control of automation software
33     prefs = {'download.default_directory': cf.get_value('download_path')}
34     chrome_options.add_experimental_option('prefs', prefs)  # Set default download path
35     # chrome_options.add_argument(r'--user-data-dir=D:\ChromeUserData')  # Set the user folder to avoid login
36     driver = webdriver.Chrome('{}\\driver\\chromedriver.exe'.format(cf.get_value('root_path')), options=chrome_options)
37     cf.set_value('driver', driver)
38 
39 
40 def main():
41     """function pytest Command start test"""
42     pytest.main(['-v', '-s', 'test_case/', '--html=report/report.html', '--self-contained-html'])
43 
44 
45 if __name__ == '__main__':
46     cf.init()  # Initialize global variables
47     get_args()  # Command line parameter parsing
48     log = Logger('szh')  # Initialize log configuration
49     set_driver()  # Initialize driver
50     main()  # Run pytest test test set
51     cf.get_value('driver').quit()  # Close selenium driver
52 
53     # First put util Mail file send_ Fill in the user name and password in mail () correctly, and then enable the send mail function!!!
54     send_mail(['22459496@qq.com'])  # Send report to mailbox

Finally, put a screenshot of the test report after running. I deliberately wrote a use case wrong. You can see that the report shows the specific error information and the screenshot of the page when the error occurs


All codes can be obtained from GitHub: https://github.com/songzhenhua/selenium_ui_auto

Keywords: Python UI

Added by f1nutter on Tue, 11 Jan 2022 14:13:34 +0200