1. 程式人生 > >不會解析配置檔案?1秒鐘應用到專案中

不會解析配置檔案?1秒鐘應用到專案中

記得......

在2003年那個夜晚,我接觸到了熱血傳奇這樣一個遊戲, 從此以後我就迷失了自我,每天沉浸在傳奇的世界裡。

你是否還記得新手村的稻草人?

你是否還記得豬洞的野豬?

你是否還記得你答應我要陪我到老嗎?

你是否還記得什麼是兄弟嗎?

無兄弟不傳奇,這句話你還記得嗎?

每天八點我們會集體攻進盟重省最豪華的宮殿-沙巴克,你還記得嗎?

好吧,我當然記得,那...... 和今天的主題有啥關係嗎?

當然有, 記得遊戲中有個F12的設定叫做內掛嗎? 我們每次玩新服或者更換電腦時都要重新設定這個內掛嗎?

今天我們就解開這個神祕的面紗,當然你要知道也先不要劇透,據說劇透一時爽,一直劇透一直爽。

好吧^-^!

其實原因很簡單

就是我們的設定通常會儲存在本地一個檔案了,那麼只要我們不更換電腦登入遊戲,那遊戲都會自動載入這個檔案裡面的設定,先看下檔案長什麼樣子。

開天斬,其它類,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秒就解決了。是不是節省了你好多時間, 哈哈哈哈哈哈哈哈哈,別罵我哈!今天就到這裡吧

&n