博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Selenium3与Python3实战 Web自动化测试框架(二)
阅读量:4584 次
发布时间:2019-06-09

本文共 37532 字,大约阅读时间需要 125 分钟。

Selenium3与Python3实战 Web自动化测试框架


 

一、项目实战中PO模型的设计与封装

 一般将所有的元素、数据都放在代码中,并不利于自动化代码的维护。最好的方式是尽量把数据、页面、操作进行分离开:PO设计模式

 PO设计模式的优势:

  1. PO提供了一种业务流程与页面元素操作分离的模式,这使得测试代码变得更加清晰。
  2. 页面对象与用例分离,使得我们更好的复用对象。
  3. 可复用的页面方法代码会变得更加优化
  4. 更加有效的命名方式使得我们更加清晰的知道方法所操作的UI元素

1、使用PO模式实现注册页面封装

1)关于配置文件

 LocalElement.py:

[RegisterElement]user_email=id>register_emailuser_email_error=id>register_email-erroruser_name=id>register_nicknameuser_name_error=id>register_nickname-errorpassword=id>register_passwordpassword_error=id>register_password-errorcode_image=id>getcode_numcode_text=id>captcha_codecode_text_error=id>captcha_code-errorregister_button=id>register-btn

 setting.py:

import osbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目首路径codeerror_path = os.path.join(base_dir,'Image','codeerror.png')  # 验证码错误图片路径code_path = os.path.join(base_dir,'Image','code.png')  # 验证码图片保存路径config_ini_dir = os.path.join(base_dir,'setting','localElement.ini')  # localElement.ini 配置文件路径

 

2)read.ini.py :用于读取LocalElement.py文件中的配置信息

from setting.setting import config_ini_dirimport configparserclass Read_Ini(object): # 初始化    def __init__(self,node = None):        if node:            self.node = node        else:            self.node = 'RegisterElement'  # 配置文件中的某个节点        self.cf = self.load_ini()    def load_ini(self):  # 加载文件        cf = configparser.ConfigParser()  # 使用 configparser模块读取配置文件信息        cf.read(config_ini_dir)  # 配置文件所在路径        return cf    def get_value(self,key): # 获取配置文件中key的value值        data = self.cf.get(self.node,key)        return data

 

3)base包下新建find_element.py文件:通过read_ini.py文件获取配置文件的信息,用于定位注册页面的目标元素

from util.read_ini import Read_Iniclass FindElement(object):    """获取元素所在位置"""    def __init__(self,driver):        self.driver = driver    def get_element(self,key):        read_ini = Read_Ini()        data = read_ini.get_value(key)        by,value = data.split('>')        try:            if by == 'id':                return self.driver.find_element_by_id(value)            elif by == 'name':                return self.driver.find_element_by_name(value)            elif by == 'className':                return self.driver.find_element_by_class_name(value)            elif by == 'xpath':                return self.driver.find_element_by_xpath(value)            else:                return self.driver.find_element_by_css(value)        except Exception as e:            # print("find_element错误信息:",e)            return None

 

4)page包新建register_page.py文件: 通过find_element.py文件,获取目标元素所在的位置

from base.find_element import FindElementclass RegisterPage(object):    """获取元素所在位置"""    def __init__(self,driver):        self.fd = FindElement(driver)        #获取邮箱元素    def get_email_element(self):        return self.fd.get_element("user_email")    #获取用户名元素    def get_username_element(self):        return self.fd.get_element("user_name")    #获取密码元素    def get_password_element(self):        return self.fd.get_element("password")    #获取验证码元素    def get_code_element(self):        return self.fd.get_element("code_text")    #获取注册按钮元素    def get_button_element(self):        return self.fd.get_element("register_button")    #获取邮箱错误元素    def get_email_error_element(self):        return self.fd.get_element("user_email_error")    #获取用户名错误元素    def get_name_error_element(self):        return self.fd.get_element("user_name_error")    #获取密码错误元素    def get_password_error_element(self):        return self.fd.get_element("password_error")    #获取验证码错误元素    def get_code_error_element(self):        return self.fd.get_element("code_text_errorr")

 

5)handle包下新建register_handle.py文件:结合register_page.py文件获取目标元素位置,再自动输入对应信息

#coding=utf-8from page.register_page import RegisterPagefrom util.get_code_value import GetCodeclass RegisterHandle(object):    """打开页面后自动输入相应信息"""    def __init__(self,driver):        self.driver = driver        self.register_p = RegisterPage(self.driver)    #输入邮箱    def send_user_email(self,email):        # self.loger.info("输入的邮箱值是:"+email)        self.register_p.get_email_element().send_keys(email)        #输入用户名    def send_user_name(self,username):        # self.loger.info("输入的用户名是:"+username)        self.register_p.get_username_element().send_keys(username)    #输入密码    def send_user_password(self,password):        # self.loger.info("输入的密码是:"+password)        self.register_p.get_password_element().send_keys(password)        #输入验证码    def send_user_code(self,file_name):        get_code_text = GetCode(self.driver)        code = get_code_text.code_online(file_name)        self.register_p.get_code_element().send_keys(code)        #获取文字信息    def get_user_text(self,info,user_info):        try:# 容错处理            if info == 'user_email_error':                text = self.register_p.get_email_error_element().text  # 获取邮箱错误信息            elif info == 'user_name_error':                text = self.register_p.get_name_error_element().text  # 获取用户名错误信息            elif info == 'password_error':                text = self.register_p.get_password_error_element().text  # 获取用户密码错误信息            else:                text = self.register_p.get_code_error_element().text  # 获取验证码错误信息        except:            text = None             return text    #点击注册按钮    def click_register_button(self):        self.register_p.get_button_element().click()        #获取注册按钮文字    def get_register_btn_text(self):        """如获取不到信息,表明页面已成功跳转"""        return self.register_p.get_button_element().text

 

 

6)business包新建register_business.py文件:测试注册页面form表单功能逻辑

from handle.register_handle import RegisterHandleclass RegisterBusiness:    """测试注册页面form表单功能情况"""    def __init__(self,driver):        self.register_h = RegisterHandle(driver)    def user_base(self,email,name,password,file_name):        self.register_h.send_user_email(email)        self.register_h.send_user_name(name)        self.register_h.send_user_password(password)        self.register_h.send_user_code(file_name)        self.register_h.click_register_button()        def register_succes(self):        if self.register_h.get_register_btn_text() == None:            # 注册成功            return True        else:            return False    # 邮箱错误    def login_email_error(self,email,name,password,file_name):        self.user_base(email,name,password,file_name)           if self.register_h.get_user_text('email_error',"请输入有效的电子邮件地址") == None:            #print("无错误,邮箱检验不成功")            return True        else:            return False               def login_name_error(self,email,name,password,file_name):        self.user_base(email,name,password,file_name)        if self.register_h.get_user_text('user_name_error',"字符长度必须大于等于4,一个中文字算2个字符") == None:            #print("用户名检验不成功")            return True        else:            return False        # 密码错误    def login_password_error(self,email,name,password,file_name):        self.user_base(email,name,password,file_name)        if self.register_h.get_user_text('password_error',"最少需要输入 5 个字符") == None:            #print("密码检验不成功")            return True        else:            return False    # 验证码错误    def login_code_error(self,email,name,password,file_name):        self.user_base(email,name,password,file_name)        if self.register_h.get_user_text('code_text_error',"验证码错误") == None:            #print("验证码检验不成功")            return True        else:            return False

 

7)case包新建first_case.py:测试注册页面form表单功能

from selenium import webdriverfrom setting import settingfrom business.register_business import RegisterBusinessclass FirstCase(object):    def __init__(self, file_name, url):        self.driver = webdriver.Chrome()        self.driver.get(url)        self.driver.maximize_window()        self.file_name = file_name        self.login = RegisterBusiness(self.driver)    def test_login_success(self):        login_success = self.login.register_succes()        if login_success:            print("注册成功,case调试失败!")    def test_login_email_error(self):        email_error = self.login.login_email_error('11111@qq.com','aaaa','111111',self.file_name)        if email_error == True:            print("Error: email没有错误提示,此条case执行失败!")    def test_login_username_error(self):        self.driver.refresh()        username_error = self.login.login_name_error('2222222@qq.com','bbbbb','111111',self.file_name)        if username_error == True:            print("Error: username没有错误提示,此条case执行失败!")    def test_login_password_error(self):        self.driver.refresh()        password_error = self.login.login_password_error('333333@qq.com','ccccc','111111',self.file_name)        if password_error == True:            print("Error: password没有错误提示,此条case执行失败!")    def test_login_code_error(self):        self.driver.refresh()        code_error = self.login.login_code_error('44444444@qq.com','dddddddd','111111',self.file_name)        if code_error == True:            print("Error: password没有错误提示,此条case执行失败!")    def main_run(self):        self.test_login_email_error()        self.test_login_username_error()        self.test_login_password_error()        self.test_login_code_error()        self.driver.close()if __name__ == '__main__':    file_name = setting.code_path    url = 'http://www.5itest.cn/register'    first_case = FirstCase(file_name,url)    first_case.main_run()

 

代码递进方向:

  read.ini.py → find_element.py → register_page.py → register_handle.py → register_business.py → first_case.py


 

附例:

get_code_value.py:获取注册页面图片,截取验证码图片部分区域,识别验证码图片,获取验证码内容

from PIL import Imagefrom setting.ShowapiRequest import ShowapiRequestimport timeclass GetCode:    """获取验证码图片,解析验证码图片并返回验证码值"""    def __init__(self, driver):        self.driver = driver    def get_code_image(self, file_name):        self.driver.save_screenshot(file_name)        code_element = self.driver.find_element_by_id("getcode_num")        left = code_element.location['x']        top = code_element.location['y']        right = code_element.size['width'] + left        height = code_element.size['height'] + top        im = Image.open(file_name)        img = im.crop((left, top, right, height))        img.save(file_name)        time.sleep(1)    # 解析图片获取验证码    def code_online(self, file_name):        self.get_code_image(file_name)        r = ShowapiRequest("http://route.showapi.com/184-4", "62626", "d61950be50dc4dbd9969f741b8e730f5")        r.addBodyPara("typeId", "35")        r.addBodyPara("convert_to_jpg", "0")        r.addFilePara("image", file_name)  # 文件上传时设置        res = r.post()        # print("test:",res.text)        time.sleep(1)        text = res.json()['showapi_res_body']        # print(text)        try:            code = text['Result']            return code        except Exception as e:            print('code_error:',e)            return None
get_code_value.py

 

二、Unittest介绍及项目实战中的运用

1、Unittest简单使用:

import unittestclass FirstCase01(unittest.TestCase):    @classmethod    def setUpClass(cls):        print("所有case执行之前的前置")    @classmethod    def tearDownClass(cls):        print("所有case执行之后的后置")    def setUp(self):        print("这个是case的前置条件")        def tearDown(self):        print("这个是case的后置调键")    @unittest.skip("不执行第一条")  # 跳过此条case 不执行    def testfirst01(self):        print("这个第一条case")    def testfirst02(self):        print("这是第二条case")        def testfirst03(self):        print("这是第3条case")    if __name__ == '__main__':    #unittest.main()    suite = unittest.TestSuite()  # suite容器    suite.addTest(FirstCase01('testfirst02'))    suite.addTest(FirstCase01('testfirst01'))    suite.addTest(FirstCase01('testfirst03'))    unittest.TextTestRunner().run(suite)
# suite :容器 ,结果是集合类型suite = unittest.defaultTestLoader.discover(case_path,'unittest_*.py')  # 批量选择性运行case#三个参数:第一个传入路径;第二个匹配文件名,成功则将该文件内指定的case存入suite容器中;第三个参数默认为Noneunittest.TextTestRunner().run(suite)    # 执行上述匹配成功的caseassertFalse(code_error, "msg")   # 用于判断结果

 

2、在项目中用HTMLTestRunner输出漂亮的HTML报告 

copy下述HTMLTestRunner代码,在项目中新建 HTMLTestRunner.py文件,将代码copy到里面即可使用

"""A TestRunner for use with the Python unit testing framework. Itgenerates a HTML report to show the result at a glance.The simplest way to use this is to invoke its main method. E.g.    import unittest    import HTMLTestRunner    ... define your tests ...    if __name__ == '__main__':        HTMLTestRunner.main()For more customization options, instantiates a HTMLTestRunner object.HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.    # output to a file    fp = file('my_report.html', 'wb')    runner = HTMLTestRunner.HTMLTestRunner(                stream=fp,                title='My unit test',                description='This demonstrates the report output by HTMLTestRunner.'                )    # Use an external stylesheet.    # See the Template_mixin class for more customizable options    runner.STYLESHEET_TMPL = '
' # run the test runner.run(my_test_suite)------------------------------------------------------------------------Copyright (c) 2004-2007, Wai Yip TungAll rights reserved.Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditions aremet:* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.* Neither the name Wai Yip Tung nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "ASIS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITEDTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR APARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNEROR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ORPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDINGNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."""# URL: http://tungwaiyip.info/software/HTMLTestRunner.html__author__ = "Wai Yip Tung"__version__ = "0.8.2""""Change History Version 0.8.2* Show output inline instead of popup window (Viorel Lupu). Version in 0.8.1* Validated XHTML (Wolfgang Borgert).* Added description of test classes and test cases. Version in 0.8.0* Define Template_mixin class for customization.* Workaround a IE 6 bug that it does not treat %(heading)s%(report)s%(ending)s """ # variables: (title, generator, stylesheet, heading, report, ending) # ------------------------------------------------------------------------ # Stylesheet # # alternatively use a
for external style sheet, e.g. #
STYLESHEET_TMPL = """""" # ------------------------------------------------------------------------ # Heading # HEADING_TMPL = """

%(title)s

%(parameters)s

%(description)s

""" # variables: (title, parameters, description) HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

""" # variables: (name, value) # ------------------------------------------------------------------------ # Report # REPORT_TMPL = """

ShowSummaryFailedAll

%(test_list)s
Test Group/Test case Count Pass Fail Error View
Total %(count)s %(Pass)s %(fail)s %(error)s
""" # variables: (test_list, count, Pass, fail, error) REPORT_CLASS_TMPL = r""" %(desc)s %(count)s %(Pass)s %(fail)s %(error)s Detail""" # variables: (style, desc, count, Pass, fail, error, cid) REPORT_TEST_WITH_OUTPUT_TMPL = r"""
%(desc)s
%(status)s
""" # variables: (tid, Class, style, desc, status) REPORT_TEST_NO_OUTPUT_TMPL = r"""
%(desc)s
%(status)s""" # variables: (tid, Class, style, desc, status) REPORT_TEST_OUTPUT_TMPL = r"""%(id)s: %(output)s""" # variables: (id, output) # ------------------------------------------------------------------------ # ENDING # ENDING_TMPL = """
"""# -------------------- The end of the Template class -------------------TestResult = unittest.TestResultclass _TestResult(TestResult): # note: _TestResult is a pure representation of results. # It lacks the output and reporting ability compares to unittest._TextTestResult. def __init__(self, verbosity=1): TestResult.__init__(self) self.stdout0 = None self.stderr0 = None self.success_count = 0 self.failure_count = 0 self.error_count = 0 self.verbosity = verbosity # result is a list of result in 4 tuple # ( # result code (0: success; 1: fail; 2: error), # TestCase object, # Test output (byte string), # stack trace, # ) self.result = [] def startTest(self, test): TestResult.startTest(self, test) # just one buffer for both stdout and stderr self.outputBuffer = io.StringIO() stdout_redirector.fp = self.outputBuffer stderr_redirector.fp = self.outputBuffer self.stdout0 = sys.stdout self.stderr0 = sys.stderr sys.stdout = stdout_redirector sys.stderr = stderr_redirector def complete_output(self): """ Disconnect output redirection and return buffer. Safe to call multiple times. """ if self.stdout0: sys.stdout = self.stdout0 sys.stderr = self.stderr0 self.stdout0 = None self.stderr0 = None return self.outputBuffer.getvalue() def stopTest(self, test): # Usually one of addSuccess, addError or addFailure would have been called. # But there are some path in unittest that would bypass this. # We must disconnect stdout in stopTest(), which is guaranteed to be called. self.complete_output() def addSuccess(self, test): self.success_count += 1 TestResult.addSuccess(self, test) output = self.complete_output() self.result.append((0, test, output, '')) if self.verbosity > 1: sys.stderr.write('ok ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('.') def addError(self, test, err): self.error_count += 1 TestResult.addError(self, test, err) _, _exc_str = self.errors[-1] output = self.complete_output() self.result.append((2, test, output, _exc_str)) if self.verbosity > 1: sys.stderr.write('E ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('E') def addFailure(self, test, err): self.failure_count += 1 TestResult.addFailure(self, test, err) _, _exc_str = self.failures[-1] output = self.complete_output() self.result.append((1, test, output, _exc_str)) if self.verbosity > 1: sys.stderr.write('F ') sys.stderr.write(str(test)) sys.stderr.write('\n') else: sys.stderr.write('F')class HTMLTestRunner(Template_mixin): """ """ def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): self.stream = stream self.verbosity = verbosity if title is None: self.title = self.DEFAULT_TITLE else: self.title = title if description is None: self.description = self.DEFAULT_DESCRIPTION else: self.description = description self.startTime = datetime.datetime.now() def run(self, test): "Run the given test case or test suite." result = _TestResult(self.verbosity) test(result) self.stopTime = datetime.datetime.now() self.generateReport(test, result) # print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)) return result def sortResult(self, result_list): # unittest does not seems to run in any particular order. # Here at least we want to group them together by class. rmap = {} classes = [] for n,t,o,e in result_list: cls = t.__class__ if not cls in rmap: rmap[cls] = [] classes.append(cls) rmap[cls].append((n,t,o,e)) r = [(cls, rmap[cls]) for cls in classes] return r def getReportAttributes(self, result): """ Return report attributes as a list of (name, value). Override this to add custom attributes. """ startTime = str(self.startTime)[:19] duration = str(self.stopTime - self.startTime) status = [] if result.success_count: status.append('Pass %s' % result.success_count) if result.failure_count: status.append('Failure %s' % result.failure_count) if result.error_count: status.append('Error %s' % result.error_count ) if status: status = ' '.join(status) else: status = 'none' return [ ('Start Time', startTime), ('Duration', duration), ('Status', status), ] def generateReport(self, test, result): report_attrs = self.getReportAttributes(result) generator = 'HTMLTestRunner %s' % __version__ stylesheet = self._generate_stylesheet() heading = self._generate_heading(report_attrs) report = self._generate_report(result) ending = self._generate_ending() output = self.HTML_TMPL % dict( title = saxutils.escape(self.title), generator = generator, stylesheet = stylesheet, heading = heading, report = report, ending = ending, ) self.stream.write(output.encode('utf8')) def _generate_stylesheet(self): return self.STYLESHEET_TMPL def _generate_heading(self, report_attrs): a_lines = [] for name, value in report_attrs: line = self.HEADING_ATTRIBUTE_TMPL % dict( name = saxutils.escape(name), value = saxutils.escape(value), ) a_lines.append(line) heading = self.HEADING_TMPL % dict( title = saxutils.escape(self.title), parameters = ''.join(a_lines), description = saxutils.escape(self.description), ) return heading def _generate_report(self, result): rows = [] sortedResult = self.sortResult(result.result) for cid, (cls, cls_results) in enumerate(sortedResult): # subtotal for a class np = nf = ne = 0 for n,t,o,e in cls_results: if n == 0: np += 1 elif n == 1: nf += 1 else: ne += 1 # format class description if cls.__module__ == "__main__": name = cls.__name__ else: name = "%s.%s" % (cls.__module__, cls.__name__) doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" desc = doc and '%s: %s' % (name, doc) or name row = self.REPORT_CLASS_TMPL % dict( style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', desc = desc, count = np+nf+ne, Pass = np, fail = nf, error = ne, cid = 'c%s' % (cid+1), ) rows.append(row) for tid, (n,t,o,e) in enumerate(cls_results): self._generate_report_test(rows, cid, tid, n, t, o, e) report = self.REPORT_TMPL % dict( test_list = ''.join(rows), count = str(result.success_count+result.failure_count+result.error_count), Pass = str(result.success_count), fail = str(result.failure_count), error = str(result.error_count), ) return report def _generate_report_test(self, rows, cid, tid, n, t, o, e): # e.g. 'pt1.1', 'ft1.1', etc has_output = bool(o or e) tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) name = t.id().split('.')[-1] doc = t.shortDescription() or "" desc = doc and ('%s: %s' % (name, doc)) or name tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL # o and e should be byte string because they are collected from stdout and stderr? if isinstance(o,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # uo = unicode(o.encode('string_escape')) # uo = o.decode('latin-1') uo = e else: uo = o if isinstance(e,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # ue = unicode(e.encode('string_escape')) # ue = e.decode('latin-1') ue = e else: ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict( id = tid, output = saxutils.escape(str(uo)+ue), ) row = tmpl % dict( tid = tid, Class = (n == 0 and 'hiddenRow' or 'none'), style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), desc = desc, script = script, status = self.STATUS[n], ) rows.append(row) if not has_output: return def _generate_ending(self): return self.ENDING_TMPL############################################################################### Facilities for running tests from the command line############################################################################### Note: Reuse unittest.TestProgram to launch test. In the future we may# build our own launcher to support more specific command line# parameters like test title, CSS, etc.class TestProgram(unittest.TestProgram): """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. """ def runTests(self): # Pick HTMLTestRunner as the default test runner. # base class's testRunner parameter is not useful because it means # we have to instantiate HTMLTestRunner before we know self.verbosity. if self.testRunner is None: self.testRunner = HTMLTestRunner(verbosity=self.verbosity) unittest.TestProgram.runTests(self)main = TestProgram############################################################################### Executing this module from the command line##############################################################################if __name__ == "__main__": main(module=None)
HTMLTestRunner

 

参考:

import unittestfrom test_mathfunc import TestMathFuncfrom HTMLTestRunner import HTMLTestRunnerif __name__ == '__main__':    suite = unittest.TestSuite()    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))    with open(r'D:\HTMLReport.html', 'wb') as f:        runner = HTMLTestRunner(stream=f,                                title='MathFunc Test Report',                                description='generated by HTMLTestRunner.',                                verbosity=2                                 )        runner.run(suite)

 

suite = unittest.TestSuite()    suite.addTest(FirstCase('test_login_success'))    # suite.addTest(FirstCase('test_login_code_error'))    suite.addTest(FirstCase('test_login_email_error'))    suite.addTest(FirstCase('test_login_username_error'))    with open(setting.report_path, 'wb') as f:        runner = HTMLTestRunner(stream=f,                                title="This is first123 report",                                description="这个是我们第一次测试报告",                                verbosity=2                                )        runner.run(suite)
demo

 * unittest中,执行case时,如果执行失败,错误信息会被保存到_outtime中,我们可通过一些技巧对这些错误信息进行操作,比如:case执行完,如果出错,可以通过tearDown方法 将错误信息页面截图,保存到指定路径,方便检查出错原因 ↓

def tearDown(self):        for method_name, error in self._outcome.errors:  # case如果执行失败,错误会保存到_outcome.errors 中            if error:  # 将错误信息截图,保存到指定路径                case_name = self._testMethodName  # case名,即定义好的方法名                report_error_name = case_name + '.png'                report_error_path = os.path.join(setting.base_dir,'report', report_error_name)                print("report_error:", report_error_name)                self.driver.save_screenshot(report_error_path)

 


 

使用unittest框架 + HTMLTestRunner 结合,优化 first_case.py 代码:

from selenium import webdriverfrom setting import settingfrom business.register_business import RegisterBusinessimport unittestimport osfrom util.HTMLTestRunner import HTMLTestRunnerclass FirstCase(unittest.TestCase):    @classmethod    def setUpClass(cls):        cls.driver = webdriver.Chrome()        cls.driver.get('http://www.5itest.cn/register')        cls.driver.maximize_window()        cls.file_name = setting.code_path        cls.login = RegisterBusiness(cls.driver)    def setUp(self):        self.driver.refresh()        # self.login = RegisterBusiness(self.driver)    def tearDown(self):        for method_name, error in self._outcome.errors:  # case如果执行失败,错误会保存到_outcome.errors 中            if error:                case_name = self._testMethodName  # case名,即定义好的方法名                report_error_name = case_name + '.png'                report_error_path = os.path.join(setting.base_dir,'report', report_error_name)                print("report_error:", report_error_name)                self.driver.save_screenshot(report_error_path)    @classmethod    def tearDownClass(cls):        cls.driver.close()    def test_login_success(self):        login_success = self.login.register_succes()        return self.assertFalse(login_success, "注册成功,case调试失败!")    def test_login_email_error(self):        email_error = self.login.login_email_error('1586457@qq.com','vvv','221111',self.file_name)        return self.assertFalse(email_error, "邮箱格式输入正确,此条case执行失败!") # 判断email_error是否为False,如是表示测试通过,如不是表示测试失败,显示参数二的信息        # if email_error == True:        #     print("Error: email没有错误提示,此条case执行失败!")    def test_login_username_error(self):        # self.driver.refresh()        username_error = self.login.login_name_error('qq.com','bbbaac','111111',self.file_name)        return self.assertFalse(username_error, "用户名格式输入正确,此条case执行失败!")    def test_login_password_error(self):        # self.driver.refresh()        password_error = self.login.login_password_error('3333666@qq.com','ccc','1114441',self.file_name)        return self.assertFalse(password_error, "密码格式输入正确,此条case执行失败!")    def test_login_code_error(self):        # self.driver.refresh()        code_error = self.login.login_code_error('44465745444@qq.com','ddfghdddd','1111',self.file_name)        return self.assertFalse(code_error, "验证码格式输入正确,此条case执行失败!")    # def main_run(self):    #    #     self.test_login_email_error()    #     self.test_login_username_error()    #     self.test_login_password_error()    #     self.test_login_code_error()    #     self.driver.close()if __name__ == '__main__':    # file_name = setting.code_path    # url = 'http://www.5itest.cn/register'    # first_case = FirstCase(file_name,url)    # first_case.main_run()    suite = unittest.TestSuite()    suite.addTest(FirstCase('test_login_email_error'))    suite.addTest(FirstCase('test_login_username_error'))    suite.addTest(FirstCase('test_login_password_error'))    suite.addTest(FirstCase('test_login_code_error'))    suite.addTest(FirstCase('test_login_success'))    with open(setting.report_path, 'wb') as f:        runner = HTMLTestRunner(stream=f,                                title="This is first123 report",                                description="这个是我们第一次测试报告",                                verbosity=2                                )        runner.run(suite)

 


 

二、数据驱动

 使用数据驱动,可以去除冗余。如同一方法传入不同参数这种,可以使用数据驱动程序,根据需求传入不同参数,驱动同一程序执行。

1、ddt安装:pip install ddt

ddt简单使用:

import ddtimport unittest@ddt.ddt  # DataTest类加ddt装饰器class DataTest(unittest.TestCase):    def setUp(self):        print("这个是setup")    def tearDown(self):        print("这个是teardown")    @ddt.data(  # ddt数据        [1,2,3,4],        [3,4],        [5,6]    )    @ddt.unpack  #传递的是复杂的数据结构时使用。比如使用元组或者列表,不是复杂的数据不需要用到unpack    def test_add(self,a,b):        print(a+b)if __name__ == '__main__':    unittest.main()

 

使用方法:

# 使用方法dd.ddt:# 装饰类,也就是继承自TestCase的类。ddt.data:# 装饰测试方法。参数是一系列的值。ddt.file_data:# 装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。# 注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。# 如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。# 如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。ddt.unpack:# 传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理

 

使用json文件:

新建文件 test_data_list.json:

[    "Hello",    "Goodbye"]

新建文件  test_data_dict.json:

{    "unsorted_list": [ 10, 12, 15 ],    "sorted_list": [ 15, 12, 50 ]}

数据驱动测试脚本ddt_test.py:

import unittestfrom ddt import ddt, file_datafrom ddt_demo.mycode import has_three_elements,is_a_greeting@ddtclass FooTestCase(unittest.TestCase):    @file_data('test_data_dict.json')    def test_file_data_json_dict(self, value):        self.assertTrue(has_three_elements(value))    @file_data('test_data_list.json')    def test_file_data_json_list(self, value):        self.assertTrue(is_a_greeting(value))        if __name__=='__main__':    unittest.main(verbosity=2)

 2、以文件的形式实现数据驱动( excel表 )

1)xlrd安装:

 pip install xlrd

2)通过xlrd 获取excel表中数据行数,将每行数据以列表形式添加到一个大列表中

import xlrdfrom setting.setting import excel_pathclass Excel_Opertion(object):    """excel表数据相关操作"""    def __init__(self,ex_path=None,index=None):        if ex_path == None:            self.excel_path = excel_path  # 默认excel文件路径        else:            self.excel_path = ex_path        if index == None:            index = 0        self.data = xlrd.open_workbook(self.excel_path)        self.table = self.data.sheets()[index] # sheets第一页数据    # 获取excel数据,按照每行一个list,添加到一个大的list里面    def get_data(self):        result = []        rows = self.get_lines()        if rows !=None:            for i in range(1,rows):                row = self.table.row_values(i)                # print(row)   # ['test001@qq.com', 'Mushishi001', '111111', 'code', 'user_email_error', '请输入有效的电子邮件地址']                result.append(row)   # [['test001@qq.com', 'Mushishi001', '111111', 'code', 'user_email_error', '请输入有效的电子邮件地址'], ['test002.com', 'Mushishi002', '111112', 'code', 'user_email_error', '请输入有效的电子邮件地址']]            return result        return None    # 获取excel行数    def get_lines(self):        rows = self.table.nrows  # 获取行数        if rows > 1:            return rows        return None

 

3)数据驱动case

 从excel表获取数据,通过ddt模块实现数据驱动模式,数据 → 驱动 → 程序,循环调用 test_register_case() 方法,直至excel表数据执行完

mport ddtimport unittestimport osfrom selenium import webdriverfrom setting import settingfrom business.register_business import RegisterBusinessfrom util.excel_operation import Excel_Opertionfrom util.HTMLTestRunner import HTMLTestRunnerex_opr = Excel_Opertion()  # 实例化ex_data = ex_opr.get_data()  # 获取excel表中每行数据@ddt.ddtclass FirstDdtCase(unittest.TestCase):    @classmethod    def setUpClass(cls):        cls.driver = webdriver.Chrome()        cls.driver.get('http://www.5itest.cn/register')        cls.driver.maximize_window()        cls.file_name = setting.code_path        cls.login = RegisterBusiness(cls.driver)    def setUp(self):        self.driver.refresh()        # self.login = RegisterBusiness(self.driver)    def tearDown(self):        for method_name, error in self._outcome.errors:  # case如果执行失败,错误会保存到_outcome.errors 中            if error:                # case_name = self._testMethodName  # case名,即定义好的方法名                report_error_name = self.assertCode + '.png'                report_error_path = os.path.join(setting.base_dir, 'report', report_error_name)                self.driver.save_screenshot(report_error_path)    @classmethod    def tearDownClass(cls):        cls.driver.close()    @ddt.data(*ex_data)    def test_register_case(self,ex_data):   # ex_data:[[],[],..] 列表套列表        """数据驱动模式,会按ex_data列表数据,一行一行循环执行,直至列表数据执行完毕"""        email, username, password, self.assertCode, assertText = ex_data  # 将ex_data每个子列表的数据按顺序赋值。验证码需要提供路径,单独给        # 邮箱、用户名、密码、验证码、错误信息定位元素、错误提示信息        register_error = self.login.register_function(email, username, password, self.file_name,self.assertCode, assertText)        self.assertFalse(register_error,"测试失败:{}".format(self.assertCode))

 

test_register_case方法:
# 数据驱动,只执行此条代码    # 邮箱、用户名、密码、验证码、错误信息定位元素、错误提示信息    def register_function(self,email,username,password,file_name,assertCode,assertText):        self.user_base(email,username,password,file_name)        if self.register_h.get_user_text(assertCode,assertText) == None:            return True        else:            return False
test_register_case

 

转载于:https://www.cnblogs.com/Eric15/articles/9847556.html

你可能感兴趣的文章
提高mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意)
查看>>
mybatis入门基础(二)----原始dao的开发和mapper代理开发
查看>>
linux网络流程分析(一)---网卡驱动
查看>>
2016年毕业设计指导与总结
查看>>
TypeError: Cannot read property 'tap' of undefined
查看>>
scikit-learn文本特征提取之TF-IDF
查看>>
WebApiTestClient自定义返回值说明
查看>>
Swift中文教程(二)--简单值
查看>>
H3C 维护命令
查看>>
根据状态变化情况,求最大值和最小值
查看>>
解决Windows10下小娜无法搜索本地应用的问题
查看>>
python2下向文件写入unicode编码的内容,codecs包
查看>>
linux 脚本统计代码行数
查看>>
Android学习笔记之短信验证码的获取和读取
查看>>
ToolBar修改返回按钮图标
查看>>
Swift - 在界面上生成81个随机红,灰色圆点(SpriteKit游戏开发)
查看>>
android UI进阶之实现listview的分页加载
查看>>
共享内存
查看>>
《Cracking the Coding Interview》——第5章:位操作——题目1
查看>>
类EF框架Chloe.ORM升级:只为更完美
查看>>