记得......

在2003年那个夜晚,我接触到了热血传奇这样一个游戏, 从此以后我就迷失了自我,每天沉浸在传奇的世界里。

你是否还记得新手村的稻草人?

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

你是否还记得猪洞的野猪?

你是否还记得你答应我要陪我到老吗?

你是否还记得什么是兄弟吗?

无兄弟不传奇,这句话你还记得吗?

每天八点我们会集体攻进盟重省最豪华的宫殿-沙巴克,你还记得吗?

好吧,我当然记得,那...... 和今天的主题有啥关系吗?

当然有, 记得游戏中有个F12的设置叫做内挂吗? 我们每次玩新服或者更换电脑时都要重新设置这个内挂吗?

今天我们就解开这个神秘的面纱,当然你要知道也先不要剧透,据说剧透一时爽,一直剧透一直爽。

好吧^-^!

不会解析配置文件?1秒钟应用到项目中 Python 第1张

其实原因很简单

就是我们的设置通常会保存在本地一个文件了,那么只要我们不更换电脑登录游戏,那游戏都会自动加载这个文件里面的设置,先看下文件长什么样子。

不会解析配置文件?1秒钟应用到项目中 Python 第2张
开天斩,其它类,1,0,1,0
布衣(男),服装类,0,0,0,0
布衣(女),服装类,0,0,0,0
木剑,武器类,0,0,0,0
炼狱,武器类,0,0,0,0
裁决之杖,武器类,0,0,0,0
怒斩,武器类,0,0,0,0
无极棍,武器类,0,0,0,0
龙纹剑,武器类,0,0,0,0
逍遥扇,武器类,0,0,0,0
霸者之刃,武器类,0,0,0,0
魔杖,武器类,0,0,0,0
骨玉权杖,武器类,0,0,0,0
龙牙,武器类,0,0,0,0
嗜魂法杖,武器类,0,0,0,0
重盔甲(男),服装类,0,0,0,0
重盔甲(女),服装类,0,0,0,0
战神盔甲(男),服装类,0,0,0,0
战神盔甲(女),服装类,0,0,0,0
天魔神甲,服装类,0,0,0,0
圣战宝甲,服装类,0,0,0,0
魔法长袍(男),服装类,0,0,0,0
魔法长袍(女),服装类,0,0,0,0
恶魔长袍(女),服装类,0,0,0,0
恶魔长袍(男),服装类,0,0,0,0
法神披风,服装类,0,0,0,0
霓裳羽衣,服装类,0,0,0,0
灵魂战衣(男),服装类,0,0,0,0
灵魂战衣(女),服装类,0,0,0,0
幽灵战衣(女),服装类,0,0,0,0
幽灵战衣(男),服装类,0,0,0,0
天尊道袍,服装类,0,0,0,1
天师长袍,服装类,0,0,0,0
骷髅头盔,首饰类,0,0,0,0
魔法头盔,首饰类,0,0,0,0
道士头盔,首饰类,0,1,0,0
黑铁头盔,首饰类,0,0,0,0
圣战头盔,首饰类,0,0,0,0
法神头盔,首饰类,0,0,0,0
天尊头盔,首饰类,0,0,0,0
天珠项链,首饰类,0,1,0,0
生命项链,首饰类,0,0,0,0
蓝翡翠项链,首饰类,0,0,0,0
绿色项链,首饰类,0,0,0,0
恶魔铃铛,首饰类,0,0,0,0
灵魂项链,首饰类,0,0,0,0
圣战项链,首饰类,0,0,0,0
法神项链,首饰类,0,0,0,0
天尊项链,首饰类,0,0,0,0
心灵手镯,首饰类,0,0,0,0
死神手套,首饰类,0,0,0,0
思贝儿手镯,首饰类,0,0,0,0
骑士手镯,首饰类,0,0,0,0
龙之手镯,首饰类,0,0,0,0
三眼手镯,首饰类,0,0,0,0
圣战手镯,首饰类,0,0,0,0
法神手镯,首饰类,0,0,0,0
天尊手镯,首饰类,0,0,0,0
龙之戒指,首饰类,0,0,0,0
红宝石戒指,首饰类,0,0,0,0
铂金戒指,首饰类,0,0,0,0
力量戒指,首饰类,0,0,0,0
紫碧螺,首饰类,0,0,0,0
泰坦戒指,首饰类,0,0,0,0
圣战戒指,首饰类,0,0,0,0
法神戒指,首饰类,0,0,0,0
天尊戒指,首饰类,0,0,0,0
新手靴,装饰类,0,0,0,0
圣战靴子,装饰类,0,0,0,0
法神靴子,装饰类,0,0,0,0
天尊靴子,装饰类,0,0,0,0
新手带,装饰类,0,0,0,0
圣战腰带,装饰类,0,0,0,0
法神腰带,装饰类,0,0,0,0
天尊腰带,装饰类,0,0,0,0
荣誉勋章13号,装饰类,0,0,0,0
荣誉勋章14号,装饰类,0,0,0,0
176原始大极品04区.超超.ItemFilter.dat

就是这个样子

如果我们更换了电脑也可以继续使用这个文件,只要把里面的数据拷贝到游戏执行的配置文件里面就可以了,我们重新再进入游戏就不用重新设置内挂的配置了

不仅内挂可以设置, 比如魔法师的魔法盾外观,释放火墙的效果我们都是可以改变的。

当然这不是我们的重点,我们的重点是自动化测试中的配置文件,同样是配置文件,只是文件的后缀不一样而已

配置文件的类型

通常自动化测试中的配置文件是以.ini 和 .conf 为后缀的文件

配置文件的组成

1.section

2.option

3.value

配置文件的格式

[section_name]
# =号可以使用:号代替
option_name=value

配置文件的注释

通常使用#号或者;分号注释,有一点一定要注意,注释最好不要写到option_name=value行的后面,否则你会遇到意想不到的错误

配置文件的作用

那么我们的配置文件主要来干些什么呢?

1.可以存储测试中测试用例使用的测试数据

2.可以存储测试中用到的资源数据,比如数据库的地址,用户,密码等等

3.可以作为ui对象库使用,存储我们ui自动化测试项目中的页面元素信息

4.可以存储项目使用的全局变量,比如项目的根目录,日志,报告的路径等等

以上这些数据均可以存放在配置文件中,方便的我们读取,当项目的一些配置信息改变时,我们只要修改配置文件即可,而不用修改具体代码,大大减小项目的维护成本!

ok,既然我都标题党了,那么现在就告诉你怎么1分钟应用到项目中。有配置文件我们必定要先解析文件才行,我们现在有这样一个配置文件,存放如下内容

[126mail_login]
loginPage.frame=xpath>//div[@id='loginDiv']/iframe
loginPage.username=xpath>//input[@name='email']
loginPage.password=xpath>//input[@name='password']
loginPage.loginBtn=xpath>//a[@id='dologin']
[126mail_homePage]
homePage.addressbook=id>_mail_tabitem_1_4text
[126mail_addContactPage]
addContactPage.newContact=xpath>//span[text()='新建联系人']
addContactPage.newName=id>input_N
addContactPage.newMail=xpath>//div[@id='iaddress_MAIL_wrap']//input[@class='nui-ipt-input']
addContactPage.newMark=xpath>//span[@class='nui-chk-text']/preceding-sibling::span/b
addContactPage.newPhone=xpath>//div[@id='iaddress_TEL_wrap']//input[@class='nui-ipt-input']
addContactPage.newComment=id>input_DETAIL
addContactPage.newCommit=xpath>//span[text()='确 定']

封装代码

下面这个封装是我之前写的,不算是很通用的功能,但是如果你的配置文件和我上面的一样用来存储ui对象库的话就完全适用了。

"""
------------------------------------
@Time : 2019/5/16 10:56
@Auth : linux超
@File : ParseConfigOld.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
------------------------------------
"""
from configparser import (
    ConfigParser,
    NoSectionError,
    NoOptionError
)
filename = 'configfile.ini'
class ParseConfigFile(object):
    """
    解析ini配置文件
    """
    def __init__(self):
        try:
            self.cf = ConfigParser() # 获取配置文件对象
            self.cf.read(filename, encoding='utf-8') # 加载配置文件到内存中
        except Exception as e:
            raise e

    def getItemsSection(self, section):
        """
        获取section下面所有section的键值
        :param sectionName:
        :return:
        """
        try:
            value = dict(self.cf.items(section))
        except (NoSectionError, KeyError):
            print('{} not exit'.format(section))
        except Exception as e:
            raise e
        else:
            return value

    def getElementValue(self, section, option):
        """根据自己的需要修改这部分代码"""
        try:
            # 我配置文件是用这个符号来分割的,所有解析的时候要使用这个符号分割,得到元素
            locator = self.cf.get(section, option).split('>')
        except (NoSectionError, NoOptionError, KeyError):
            print('section:{} or option:{} not exit'.format(section, option))
        except Exception as e:
            raise e
        else:
            return locator # 获取option键对应的value

    def getAllSections(self):
        try:
            all_sections = self.cf.sections()
        except Exception as e:
            raise e
        else:
            return all_sections # 所有的sections返回值是个列表


if __name__=='__main__':
    cf = ParseConfigFile()
    locator = cf.getElementValue('126mail_login','loginPage.username')
    print(locator)
    print(cf.getItemsSection('126mail_login'))
    print(cf.getAllSections())

封装改进

下面的封装几乎可以完成任何自动化测试项目中配置文件存储任何数据类型的数据解析

"""
------------------------------------
@Time : 2019/5/16 9:11
@Auth : linux超
@File : ParseConfigFile.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
------------------------------------
"""
from configparser import (
    ConfigParser,
    NoOptionError,
    NoSectionError
)


class ParseConfigFile(ConfigParser):
    def __init__(self, filename):
        super().__init__()
        try:
            self.filename = filename
            self.read(filename, encoding='utf-8')
        except Exception as e:
            raise e

    def get_all_option(self, section='DEFAULT'):
        """获取指定section下所有的option"""
        try:
            options = self.options(section)
            return options
        except NoSectionError:
            print('NoSectionError : {} not exist'.format(section))
        except Exception as e:
            raise e

    def get_value(self, section='DEFAULT'):
        """获取指定section中所有的option和value,返回一个字典"""
        try:
            value = dict(self.items(section))
            return value
        except (NoSectionError, KeyError):
            print('{} not exist'.format(section))
        except Exception as e:
            raise e

    def get_option_value(self, section, option, flag=False):
        """
        获取指定section和option对应的数据
        如果option对应数据为数字,则自动转换为int或者float
        如果option对应的数据是个可以使用eval转换的类型,则传递flag为True时,自动转换,否则输出str
        """
        try:
            value = self.get(section, option)
            if value.isdigit():
                return int(value)
            try:
                return float(value)
            except Exception:
                pass
            if isinstance(flag, bool) and flag:
                return eval(value)
            return value
        except (NoSectionError, NoOptionError, KeyError):
            print('no option "{}" or section "{}"'.format(option, section))
        except Exception as e:
            raise e

    def __call__(self, section='DEFAULT', option=None, flag_eval=False, flag_bool=False):
        """
        对象当成函数使用的时候会默认调用这个方法
        这个方法可以实现上面多数功能
        :param section:
        :param option:
        :param flag_eval: 如果为True 我们使用eval转换类型
        :param flag_bool: 如果为True 我们使用把数据转换为bool
        :return:
        """
        if option is None:
            return dict(self[section])
        if isinstance(flag_bool, bool):
            if flag_bool:
                return self.getboolean(section, option)
        else:
            raise ValueError('{} must be type bool'.format(flag_bool))
        data = self.get(section, option)
        if data.isdigit():
            return int(data)
        try:
            return float(data)
        except Exception:
            pass
        if isinstance(flag_eval, bool):
            if flag_eval:
                return eval(data)
        else:
            raise ValueError('{} must be type bool'.format(flag_eval))
        return data

if __name__ == '__main__':
    conf = ParseConfigFile('configfile.ini')
    print('所有的option', conf.get_all_option('FilePath'))
    print('获取section:{},option:{}对应的数据:{}'.
          format('FilePath', 'TestCase', conf.get_option_value('FilePath', 'TestCase')))
    print('获取section:{}下所有的键值{}'.format('FilePath', conf.get_value('ExcelNum')))
    print(conf())
    print(conf(section='FilePath', option='TestCase'))
    print(conf(option='a', flag_bool=True))
    print(conf(section='ExcelNum', option='Actual_Column_Num', flag_eval=True))

1分钟应用带项目中

啥? 你还不知道怎么一分钟应用到项目中?

好吧,看来是逃不过去了。 我要说了..... 首先复制代码,当然你已经知道上述代码的含义, 在你的项目中新建py文件,拷贝代码到你的文件中,ok接下来你可能已经知道怎么用了。这能有1分钟吗?

应该30秒就解决了。是不是节省了你好多时间, 哈哈哈哈哈哈哈哈哈,别骂我哈!今天就到这里吧

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄