其他测试框架 一款适用于 webdriver,appium 的框架

· 2016年06月10日 · 最后由 kekocool 回复于 2018年03月14日 · 3009 次阅读

前言

  • 个人做 UI 自动化 2 年多了,从 webdriver 做到 appium。从最基础的脚本自学开始。但是社区我还是会继续写帖子的,也希望我的帖子能跟大家交流,请教,提高自己,帮助大家
  • 本框架适用于 webdriver,跟 appium。本来我的代码是线性的,但是在前辈 (杰哥) 的教诲下,了解到代码是需要可持续,可维护,可扩展的。

代码结构

  • 核心类 (base)
  • 基础配置类 (Terminal)
  • 用例功能操作类 (module)
  • 数据类 (date)
  • 外置元素配置文件 (elements.py)
  • 用例类 (case)

  • 继承关系 base->Terminal->module->case

  • 外置元素配置文件 (elements.py) 可应用到 module,case 类中;date 类主要是 case 类的参数设置

核心代码

  • 核心类 (base)
class BaseHandle(object):
    def __init__(self):
        self._testcase_list = []
        self._case_name_length = 0
        self._line_length = 100
        self.finsh_flag = False
        self.stdout = HandleStream(Queue.Queue())

    def __iter__(self):
        return iter(self._testcase_list)

    @classmethod
    def setUpClass(cls):
        "Hook method for setting up class fixture before running tests in the class."
        pass

    @classmethod
    def tearDownClass(cls):
        "Hook method for deconstructing the class fixture after running all tests in the class."
        pass

    def setUp(self):
        "Hook method for setting up the test fixture before exercising it."
        pass

    def tearDown(self):
        "Hook method for deconstructing the test fixture after testing it."
        pass

    def add_case(self,casename,func,*args,**kwargs):
        if isinstance(func, basestring):
            raise TypeError("tests must be an iterable of tests, not a string")

        if not hasattr(func, '__call__'):
            raise TypeError("{} is not callable".format(repr(func)))

        casename_length = len(casename)
        if casename_length > self._case_name_length:
            self._case_name_length = casename_length
        self._testcase_list.append((casename,func,args,kwargs))

    def run(self):
        self.stdout.write('用例开始执行')
        self.stdout.write(self.split_line)
        try:
            self.setUpClass()
        except:
            return [('setUpClass Faile',False)]

        for case in self._testcase_list:
            r = {}
            try:
                self.stdout.write(case[0])
                self.setUp()
                r = case[1](*case[2],**case[3])
            except Exception,e:
                 r['result'] = False
                 r['message'] = e.message
            finally:
                try:
                    self.tearDown()
                finally:
                    space_count = self._line_length - len(case[0].decode('utf-8')) - 4
                    if r['result']:
                        result = 'PASS'
                    else:
                        result = 'FAIL'
                    msg = '%s%s%s' %(case[0],' '*space_count,result)
                    self.stdout.write(msg)
                    if result == 'FAIL':
                        self.stdout.write('%s :: 失败原因: %s' %(case[0],r['message']))

                    self.stdout.write(self.split_line)
        try:
            self.tearDownClass()
        finally:
            self.finsh_flag = True

  • 基础配置类 (Terminal)
class TerminalHandle(BaseHandle):
    def __init__(self,desired_capabilities,browerName,host,port=None,timeout=None,**kwargs):
        super(TerminalHandle,self).__init__()
        if not timeout:
            self.timeout = 30
        if not port:
            self.port = 4444
        else:
            self.port = port
        if not host:
            self.host = '127.0.0.1'
        else:
            self.host = host
        if not desired_capabilities:
            self.desired_capabilities=DesiredCapabilities.CHROME
        else:
            self.desired_capabilities = {
                'platformName':browerName,
                #'autoLaunch':False
            }
        if kwargs:
            self.desired_capabilities.update(kwargs)

        while True:
            if self.conn_webdrive():
                break
            time.sleep(1)

    def conn_webdrive(self):
        try:
            self.driver = webdriver.Remote('http://%s:%d/wd/hub' % (self.host,self.port),self.desired_capabilities)
            return True
        except Exception,e:
            print str(e)
            return False

    def setUp(self):
        #self.driver.launch_app()
        pass

    # def tearDown(self):
    #     "Hook method for deconstructing the test fixture after testing it."

    @classmethod
    def setUpClass(cls):
        #cls.driver.launch_app()
        pass

    #@classmethod
    def tearDownClass(self):
        "Hook method for deconstructing the class fixture after running all tests in the class."
        self.driver.close()
        pass

    def find(self,loc):
        try:
            WebDriverWait(self.driver,self.timeout).until(lambda drive:drive.find_element(*loc).is_displayed())
            return self.driver.find_element(*loc)
        except Exception,e:
            print u"%s 页面中超时%ds未能找到 %s 元素%s" %(self,self.timeout,loc,e)

    def click_keys(self,loc):
        self.find(loc).click()

    def clear_keys(self,loc):
        self.find(loc).clear()

    def send_keys(self,loc,value):
        sleep(3)
        self.find(loc).send_keys(value)

    def click_button(self,loc):
        self.find(loc).click()

    def isElement(self,identifyBy,c):
        #self.drive.implicitly_wait(60)
        flag=None
        try:
            if identifyBy == "id":
                #self.driver.implicitly_wait(60)
                self.driver.find_element_by_id(c)
            elif identifyBy == "xpath":
                #self.driver.implicitly_wait(60)
                self.driver.find_element_by_xpath(c)
            elif identifyBy == "class":
                #self.driver.implicitly_wait(60)
                self.driver.find_element_by_class_name(c)
            flag = True
        except NoSuchElementException,e:
            flag = False
        finally:
            return flag

    def screenshot(self):
        path = "D:\\"
        title = "appium_test_result"
        timestr = time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))
        new_path = os.path.join(path, title)
        if not os.path.isdir(new_path):
            os.makedirs(new_path)
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_" + timestr + ".png")
        else:
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_" + timestr + ".png")

    def screenshot_True(self):
        path = "D:\\"
        title = "appium_test_result"
        timestr = time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))
        new_path = os.path.join(path, title)
        if not os.path.isdir(new_path):
            os.makedirs(new_path)
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_True_" + timestr + ".png")
        else:
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_True_" + timestr + ".png")

    def screenshot_Error(self):
        path = "D:\\"
        title = "appium_test_result"
        timestr = time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))
        new_path = os.path.join(path, title)
        #print 1
        if not os.path.isdir(new_path):
            os.makedirs(new_path)
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_Error_" + timestr + ".png")
        else:
            self.driver.get_screenshot_as_file(new_path+"\\"+"result_Error_" + timestr + ".png")
  • 用例功能操作类 (module)
class ModuleHandle(TerminalHandle):
    def __init____init__(self,desired_capabilities,browerName,host,port=None,timeout=None,**kwargs):
        super(ModuleHandle,self).__init__(desired_capabilities,browerName,host,port=None,timeout=None,**kwargs)

    #进入我的按钮
    def mybutton(self):
        pass
  • 用例类 (case)
import Date
class Case(TerminalHandle.AppHandle,TerminalHandle.Data):
    def __init____init__(self,desired_capabilities,browerName,host,port=None,timeout=None,**kwargs):
        super(Case,self).__init__(desired_capabilities,browerName,host,port=None,timeout=None,**kwargs)

    #调用数据
    testdata=Date.Data()

    #登录功能    包含传入参数,期望值,实际值,期望跟实际值的比对
    def login(self,phone_num=None,pwd=None,expect=True,ending=None,*args):
        s
        actual=True
        code=111
        try:
            if self.isElement("xpath", kalemao.login_sgin)==True:
                #实际值
                e='用户已登录'
                actual=True
            else:
                    pass
        except Exception,e:
            actual=False
        finally:
            result=str(e)==str(expect)
            ending={'code':code,'message':e,'result':result}
            return ending
        pass


     def login_test1(self,phone_num=.phone_num,passwd=testdata.passwd,expect='登录成功',*args):
        return self.login(phone_num,passwd,expect)

    #运行case
if __name__ == '__main__':
    t = Case('','','localhost',4444,'')
    t.add_case('登陆成功',t.login_test1)

    def start():
        t.run()

总结

  • 该框架暂时没有想到解决测试报告的问题,如何把 Htmlrunner 整合到框架中,暂时没有什么好的想法。该框架也适用 appium,只要把init中的参数修改一下就可以了
  • 后续会支持断言,断言对于测试来说还是比较重要的
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 6 条回复 时间 点赞
#1 · 2016年06月10日 Author

有需要讲清楚的请大家留言,自己顶一个

—— 来自 TesterHome 官方 安卓客户端

能说一下这个做这个框架的初衷是什么(例如要实现某个需求,而经过调研目前主流的哪些框架均不支持/难以通过简单修改来支持),为何要这么设计吗?对这个比较感兴趣。

#4 · 2016年06月11日 Author

#2 楼 @chenhengjie123 1.也可以适用于 appium(ios,adnroid),因为就是核心是init里的参数配置。android 跟 ios 通用init(self,platformName,platfromVersion,deviceName,udid,appPackage,appActivity,host,port=None,timeout=None,**kwargs)

2.在 case 里关注 case 的场景,传参,预期结果,实际结果,预期跟实际的比对。(可以通过判断 UI 元素是否存在的要点来判断预期实际结果)

3.元素分离,更好的维护脚本。在基础配置类中封装的 find 函数

很好的思路~

有图的话看起来更直观一点😀

仅楼主可见
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册