前言

之前已经发过一个,许多人给提了很宝贵的意见,根据大家的意见和自己的一点思考,对原来的框架进行了一点修改,这里给大家分享一下,还是请各位看完后多多提意见。

结构

大体的结构没有太大的变化,这里附上原帖地址 https://testerhome.com/topics/3460
修改的地方有以下几个:
1.在 testSet 下增加了一个 bsns 文件夹,里面有 bsnsCommon.py;element.xml;TestCase.xls3 个文件夹
2.common 里面增加 AppiumServer.py;将 myPhone.py 变为 init.py
3.增加了 autoRun.bat 和 install.bat

修改处

1.讲 AppiumServer 从 run.py 中抽离出来,封装成了 AppiumServer.py
2.弃用自己写的 Log 方法,使用了 python 自带的 logging
3.讲 element 的路径配置进了 element.xml 中
4.实现了测试数据参数化
5.做成了 bat 文件调用 run.py
6.修改了注释风格

下面仔细说一下

AppiumServer

这个借鉴了 cosyman 以前发过的帖子,原帖地址 https://testerhome.com/topics/1864
总结而言就是原先用线程做的现在改成了进程。代码如下:

class AppiumServer:

    def __init__(self):
        global appiumPath, baseUrl
        appiumPath = readConfigLocal.getConfigValue("appiumPath")
        baseUrl = readConfigLocal.getConfigValue("baseUrl")

    def startServer(self):
        """start the appium server
        :return:
        """
        cmd = self.getCmd()
        t1 = runServer(cmd)
        p = Process(target=t1.start())
        p.start()

    def stopServer(self):
        """stop the appium server
        :return:
        """
        #kill myServer
        os.system('taskkill /f /im node.exe')

    def reStartServer(self):
        """reStart the appium server
        :arg:
        :return:
        """
        self.stopServer()
        self.startServer()

    def isRunnnig(self):
        """Determine whether server is running
        :return:True or False
        """
        response = None
        url = baseUrl+"/status"
        try:
            response = urllib.request.urlopen(url, timeout=5)

            if str(response.getcode()).startswith("2"):
                return True
            else:
                return False
        except URLError:
            return False
        finally:
            if response:
                response.close()

    def getCmd(self):
        """get the cmd of start appium server
        :return:cmd
        """
        rootDirectory = appiumPath[:2]
        startCMD = "node node_modules\\appium\\bin\\appium.js"

        cmd =rootDirectory+"&"+"cd "+appiumPath+"&"+startCMD

        return cmd

import threading


class runServer(threading.Thread):

    def __init__(self, cmd):
        threading.Thread.__init__(self)
        self.cmd = cmd

    def run(self):
        os.system(self.cmd)

if __name__ == "__main__":
    oo = AppiumServer()
    oo.startServer()

Log

原先是自己写的 log 方法,现在是使用了 python 自带的 logging,部分代码如下:

self.logger = logging.getLogger()
self.logger.setLevel(logging.INFO)

#create handler,write log
fh = logging.FileHandler(os.path.join(logPath, "outPut.log" ))
#Define the output format of formatter handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

self.logger.addHandler(fh)

这里我并没有使用 logging 的配置文件,而是直接写在的代码中

element.xml

这里也是借鉴了 xushizhao 的帖子,原帖地址如下 https://testerhome.com/topics/2937
为什么要这样做我就不多说,原帖里都说了,代码如下:
1.element.xml
与原帖不同的是我在 element 标签外面添加了一个 activity 标签,这样就可以不必要同个标签配置多次了。

<activity name = "GuideActivity"><!--activity名称-->

       <element name="welcome"> <!-- 元素对象 -->
            <name>Welcome</name><!-- 元素名称 -->
            <type>RelativeLayout</type><!-- 元素类型 input/button -->
            <pathtype>ID</pathtype><!-- 获取元素的模式 ID/XPATH/CLASSNAME/NAME-->
            <pathvalue>ag_ll_dotlayout</pathvalue><!-- 元素定位路径 -->
        </element>

</activity >

2.调用方法

activity = {}
def setXml():
    """
    get the xml file's value
    :use:
    a = getXml(path)

    print(a.get(".module.GuideActivity").get("skip").get("type"))
    :param: xmlPath
    :return:activity
    """
    if len(activity) == 0:
        xmlPath = os.path.join(readConfig.prjDir, "testSet\\bsns", "element.xml")
        # open the xml file
        per = ET.parse(xmlPath)
        allElement = per.findall('activity')

        for firstElement in allElement:
            activityName = firstElement.get("name")

            element = {}
            for secondElement in firstElement.getchildren():
                elementName = secondElement.get("name")

                elementChild = {}
                for thirdElement in secondElement.getchildren():

                    elementChild[thirdElement.tag] = thirdElement.text

                element[elementName] = elementChild
            activity[activityName] = element

def getElDict(activityName, elementName):
    """
    According to the activityName and elementName get element
    :param activityNmae:
    :param elementName:
    :return:
    """
    setXml()
    elementDict = activity.get(activityName).get(elementName)
    return elementDict

class element:

    def __init__(self, activutyName, elementName):
        global driver
        driver = myDriver.GetDriver()
        self.activutyNmae = activutyNmae
        self.elementName = elementName
        elementDict = getElDict(self.activutyNmae, self.elementName)
        self.pathtype = elementDict.get("pathtype")
        self.pathvalue = elementDict.get("pathvalue")

    def isExist(self):
        """
        To determine whether an element is exits
        :return: TRUE or FALSE
        """
        try:
            if self.pathtype == "ID":
                driver.find_element_by_id(self.pathvalue)
            if self.pathtype == "CLASSNAME":
                driver.find_element_by_class_name(self.pathvalue)
            if self.pathtype == "XPATH":
                driver.find_element_by_xpath(self.pathvalue)
            if self.pathtype == "NAME":
                driver.find_element_by_name(self.pathvalue)
        except NoSuchElementException:
            return False
        return True

    def doesExist(self):
        """
        To determine whether an element is exits
        :return:
        """
        i = 1
        while not self.isExist():
            sleep(1)
            i = i+1
            if i >= 10:
                return False
        else:
            return True

    def get(self):
        """
        get one element
        :return:
        """
        if self.doesExist():
            if self.pathtype == "ID":
                element = driver.find_element_by_id(self.pathvalue)
                return element
            if self.pathtype == "CLASSNAME":
                element = driver.find_element_by_class_name(self.pathvalue)
                return element
            if self.pathtype == "XPATH":
                element = driver.find_element_by_xpath(self.pathvalue)
                return element
            if self.pathtype == "NAME":
                element = driver.find_element_by_name(self.pathvalue)
                return element
        else:
            return None

    def gets(self, index):
        """
        get one element in elementList
        :return:
        """
        if self.doesExist():
            if self.pathtype == "ID":
                elements = driver.find_elements_by_id(self.pathvalue)
                return elements[index]
            if self.pathtype == "CLASSNAME":
                elements = driver.find_elements_by_class_name(self.pathvalue)
                return elements[index]
            if self.pathtype == "XPATH":
                elements = driver.find_elements_by_xpath(self.pathvalue)
                return elements[index]
            if self.pathtype == "NAME":
                elements = driver.find_elements_by_name(self.pathvalue)
                return elements[index]
            return None
        else:
            return None

    def click(self):
        """
        click element
        :return:
        """
        try:
            el = self.get()
            el.click()
        except AttributeError:
            raise

    def clicks(self, index):
        """
        click element
        :return:
        """
        try:
            el = self.gets(index)
            el.click()
        except AttributeError:
            raise

    def sendKey(self,values):
        """
        input the key
        :return:
        """
        try:
            el = self.get()
            el.clear()
            el.send_keys(values)
        except AttributeError:
            raise

    def sendKeys(self, index, values):
        """
        input the key
        :return:
        """
        try:
            el = self.gets(index)
            el.clear()
            el.send_keys(values)
        except AttributeError:
            raise

    def getAttribute(self, attribute):
        """
        get the element attribute
        :param attribute:
        :return:value
        """
        el = self.get()
        value = el.get_attribute(attribute)
        return value

根据 anctivityName 和 elementName 获取 element,使用的时候可以这样用:element(anctivityName ,elementName).click()

测试数据参数化

这里我是使用了 ParamUnittest,官网地址如下大家可以咨询下载:https://pypi.python.org/pypi/ParamUnittest#downloads
将测试数据配置到 excel 里面,然后读取。代码如下:

读取 excel

import xlrd
cls = []
def getXLS(sheetName):
    """
    get the value in excel
    :param sheetName
    :return:cls
    """

    if len(cls) == 0:
        xlsPath = os.path.join(readConfig.prjDir, "testSet\\bsns", "TestCase.xls")

        #read the excel
        data = xlrd.open_workbook(xlsPath)

        #get the sheet
        table = data.sheet_by_name(sheetName)

        nrows = table.nrows

        for i in range(nrows):

            if table.row_values(i)[0] != 'userName':
                cls.append(table.row_values(i))
    return cls

ParamUnittest 的使用

loginCls = getLoginCls()

@user1trized(
        *loginCls
    )

class TestBar(paramunittest.ParametrizedTestCase):


    def setParameters(self, userName,password,result):
        self.userName = userName
        self.password = password
        self.result = result


    def runTest(self):
        print(self.userName, self.password, self.result)

ParamUnittest 的例官网里面有好多,大家可以自己去研究。

bat

本来是想在 bat 文件里面开启 server,识别安装软件,后来发现自己的能力有限,要考虑的东西有点多,后来放弃了,改在 py 文件里面完成,然后仅在 bat 文件里面调用。

ECHO START INSTALL
F:  
cd F:\testApp01  
start pythonw testSet\init.py
ECHO END INSTALL
PAUSE 

总结

1.生成的报告不太满意,目前还是自己实现的,不知道各位有什么好的推荐?
2.异常机制依旧没有完善的太好,继续努力。
3.论坛里面有太多好帖子了,感觉大神的分享。
4.希望大家在看完帖子后可以留下你的意见,感谢!!!


↙↙↙阅读原文可查看相关链接,并与作者交流