如何记录selenium自动化测试过程当中接口的调用信息

上一篇博客,我写了python自动化框架的一些知识和粗浅的见解,在上一篇中我也给本身提出一个需求:若是记录在测试过程当中接口的调用状况?提出这个需求,我以为是有意义的。你在测试过程当中确定会遇到一些莫名其妙的问题,好比:web某个页面一直在刷进度条,致使你定位元素失败,可是,你再手动操做一遍可能没法复现....对于咱们来讲,确定会遇到许多相似的问题。你会发现有时候仅仅靠一张截图,你远远找不到bug的缘由。这时候,我在想若是我能拿到这一系列操做所调用的接口信息多好,我就能明白为何发生这种问题了。好比一直在刷进度条我以为有几种状况:1.后端一直在等待某个接口的响应信息。2.网络缘由致使,接口响应很慢(局域网通常不多出现这类问题)、3.前端工程师没有动态的把这个进度条display="None"....不论何种缘由,我拿到相关的接口信息,就能对错误逐个排除。好比我发现某个接口的响应时间很长.....或者全部接口的响应的时间可能是大于1s的,又或者都正常响应,原来js没有动态改变进度的属性?反正不管如何我拿到自动化操做的接口信息是没有坏处的吧?(小小的缺点我后面提到)css

那么问题是,咱们如何精准的拿到这些信息?我开始的想法是经过firebug去拿,firebug咱们平时用的也比较多,能够方便的看到控制台信息(js的执行状况)和网络信息(接口调用状况),可是我查了不少资料都没有办法完整的把这些信息给导出来....可是,我很快的想到了Fiddler。Fiddler是目前为止我用的最好最顺手的一款http抓包工具(不要和我提什么wireshark,虽然通过网卡的信息它都能抓可是仅对http协议来讲,真不如fiddler牛逼,谁用谁知道),更重要的是因为它是个代理服务器,因此能抓任何设置其为代理的终端,包括手机...想到这,心中一阵窃喜。下面我先说说思路,而后再详细的说明,我是怎么作的。个人思路以下:html

1.设置fiddler过滤一下抓取信息,如:只抓取host为:*.csdn.net的接口信息。前端

2.测试执行开始前,打开fiddler。
python

3.当执行一个test时,先在fiddler控制台输入cls,清空当前sessions,防止接口信息过多或混在一块儿不方便排查错误。web

4.当执行test完毕,若是有错误,则保存此test执行过程当中的全部sessions至一个文件夹。无错误不作操做(若是你非要保存也是能够的)正则表达式

5.重复2-3的步骤,直至全部测试结束。windows

6.测试执行结束后,关闭fiddler。后端

上面的想法,其实也是很简单的,咱们再一个个看看如何实现:
浏览器

对于步骤1/2/5  用python调用控制台打开fiddler是有问题的(主进程会阻塞,其余应用程序没问题),改用AutoIt的run方法,关闭没问题。安全

对于3/4是要想一想办法的。对于自动化人员来讲AutoIt您应该是接触过了,若是没有就去看看吧!AutoIt有弊端有优势,最大的优势就是编写简单、脚本能转换成exe.最大的缺点:windows非标准控件没法获取。万幸的是Fiddler的控制台输入框能被AutoIt识别!还有就是如何改写Fiddler的Scripts。(咱们的需求很简单,别被吓到了)

因此第一步:咱们编写清除fiddler session的脚本,转换成C_interface.exe。脚本简单到不能简单了,以下:

 

Example()
Func Example()
    Local $hWnd = WinWait("[Title:Telerik Fiddler Web Debugger]", "", 10)
	WinActivate($hWnd);激活当前窗口
    ControlFocus($hWnd, "","[CLASS:WindowsForms10.EDIT.app.0.141b42a_r6_ad1;NAME:txtExec]")
    ControlSetText($hWnd, "", "[CLASS:WindowsForms10.EDIT.app.0.141b42a_r6_ad1;NAME:txtExec]","cls")
	Send("{ENTER}")
 EndFunc   ;

 

 

 照顾一下,刚开始看AutoIt的同窗,Title中的Telerik Fiddler Web Debugger与ControlFocus中的CLASS、NAME是经过AutoIt Window info这个工具捕捉的,咱们的可能不同贴图一张:

咱们写完了了清除session后,再来写下保存接口信息的脚本,也很简单保存为D_interface.exe:

Example()
Func Example()
    Local $parment=$CmdLine[1];接受控制台数据,$parment为fiddler接口保存路径
    Local $hWnd = WinWait("[Title:Telerik Fiddler Web Debugger]", "", 10)
    WinActivate($hWnd);激活当前窗口
    ControlFocus($hWnd, "","[CLASS:WindowsForms10.EDIT.app.0.141b42a_r6_ad1;NAME:txtExec]")
    ControlSetText($hWnd, "", "[CLASS:WindowsForms10.EDIT.app.0.141b42a_r6_ad1;NAME:txtExec]","dump "&$parment)
    Send("{ENTER}")
    IF WinActive("[Title:Cannot Save SAZ]") Then
      ControlClick("[Title:Cannot Save SAZ]","","Button1")
    EndIf
EndFunc   ;

标红部分的解释是:当Fiddler没有session时(虽然不太可能出现这种状况),执行dump命令会弹出个对话框,这时候要关闭对话框!!若是不关闭的话下面对fiddler的操做会出现问题,由于这时候弹出框是fiddler的顶级窗口,可能致使脚本中使用Enter键无效...

其次,因为python调用控制台启动Fiddler有问题(具体问题缘由未知),因此咱们也用AutoIt编写,并转换成S_interface.exe:

Example()
Func Example()
    Local $parment=$CmdLine[1]
	Run($parment)
EndFunc   ;

最后,咱们改下Fiddler的Script的,从菜单的Rules->Customer Rules打开脚本剪辑器,直接拉到script的末端修改方法OnExecAction以下:

   ......
        case "dump":
            UI.actSelectAll();
            var bpMethod = sParams[1]
            //UI.actSaveSessionsToZip(CONFIG.GetPath("Captures") + "dump.saz");
            UI.actSaveSessionsToZip(bpMethod)

            FiddlerObject.StatusText = "Dumped all sessions to " +bpMethod;
            //FiddlerObject.alert(bpMethod);
            UI.actRemoveAllSessions();
            return true;

修改case 'dump'的状况,bpMethod是由命令bump空格后的参数。对应于上文咱们AutoIt脚本中的$parment参数(由控制台输入)。

上面咱们的准备工做的作的差很少了,总结一下,干了下面的几个事情:

1.用AutoIt生成了清除Fiddler session的一个exe

2.用AutoIt生成了保存Fiddler session的一个exe

3.修改了Fiddler的Script接受一个保存session路径的一个参数

在完成了以上工做后,咱们来进行测试!注意:在此以前咱们要明白一些事情:

1.用Fiddler作代理后,可能影响接口的加载速度,毕竟有个第三者。可是我以为速度影响在web自动化上不是那么重要的事情,毕竟现实中的访问速度确定比你公司内部访问速度更差。(缺点之一)

2.用Fiddler作代理后,咱们知道在访问https的时候好比访问百度,可能显示非安全连接,咱们日常的作法是把fiddler的证书导入浏览器(具体百度上有说明),可是咱们webdriver启动的是个空白的浏览器,如何能自动加载Fiddler证书

3.用Fiddler作代理后,若是Fiddler崩溃或者没启动起来形成没法联网致使全部脚本没法运行,这个风险咱们如何规避?

第一个问题跳过,咱们看看第二个问题

在路径C:\Python27\Lib\site-packages\selenium\webdriver\firefox\firefox_profile.py下定义了一个FirefoxProfile类,这个类咱们平时可能不太用的上,可是用不上不表明他不重要,这个类是个管理浏览器插件的类。咱们说明一下:

1.其构造函数传火狐浏览器的插件路径。火狐浏览器的插件通常在C:\Users\***\AppData\Roaming\Mozilla\Firefox\Profiles\****.default-*****"这个路径下面。构造函数会把这个路径下的东西copy到c:\\users\\pf-211x3\\appdata\\local\\temp\\***\\webdriver-py-profilecopy这个文件夹下。

2.encoded函数。这个函数的文档属性这样解释:"A zipped, base64 encoded string of profile directory for use with remote WebDriver JSON wire protocol"具体很么意思呢?就是这个函数会把上文中咱们提到的c:\\users\\pf-211x3\\appdata\\local\\temp\\***\\webdriver-py-profilecopy这个文件夹压缩成ZIP格式文件,而后对这个文件进行base64的编码,当启动浏览器的时候,会将这个编码一同发给服务器,服务器再对他base64解码、解压缩将您本地火狐插件完完整整的复制到新启动的空白浏览器上,那么咱们新启动的浏览器就拥有了本地浏览器全部的插件了。

3.set_preference。传递一个键值对,就是设置火狐浏览器的选项,好比设置代理等等....

4.add_extension。传递一个***.xpi的路径,就是设置浏览器加载的插件,好比启动浏览器加载firebug,把firebug插件路径传递给add_extension便可

通过我对FirefoxProfile类的说明,您大概知道了问题二的解决办法了吧,对的就是向FirefoxProfile类中传递插件的路径。可是C:\Users\***\AppData\Roaming\Mozilla\Firefox\Profiles\****.default-*****"这个文件是比较大的反正个人是50M,将这样一个大的文件通过步骤2的操做,是个费事费力的事情。因此大家会发现,若是把完整的插件路径传递给FirefoxProfile,通过一系列的压缩、传递,启动本地浏览器会很是很是慢!通过排除和尝试法,我发现火狐对证书的控制是由插件文件夹下的cert8.db控制的,全部咱们把这个文件给拷贝出来放在一个文件夹中,单独传这个文件夹路径便可。

第三个问题:

浏览器的代理有几下几种:1.不使用代理。2.自动检测此网络的代理设置。3.使用系统代理。4.手动配置代理。5,自动代理配置

对于1和4你们都明白;对于5也还好就是:写一个脚本告诉浏览器什么样的域名我要代理,其余的不使用代理(具体百度);对于2和3我多少有点不知道他怎么用,对于3使用系统代理个人实践就是若是我启动了fiddler它就使用了fiddler代理,若是没有启动就不使用代理,看起来挺智能了。我也不太清楚这样为何...因此对于问题三我也是纠结的:第1、若是设置手动代理看起来是没问题的,就怕fiddler故障了,而后雪崩...第2、我着实不太了解我使用系统代理对不对?这个你们本身看好了。我反正就用系统代理了,至少能知足个人想法:万一fiddler故障了也没啥,大不了就抓不到接口数据呗,其余的还能正常的跑....

最后,就是在咱们上篇继承unnitest的run方法里面,修改一点点代码,也很简单(红色标识了):

 

......
def
run(self, result=None): orig_result = result if result is None: result = self.defaultTestResult() startTestRun = getattr(result, 'startTestRun', None) if startTestRun is not None: startTestRun() self._resultForDoCleanups = result screenshot_path=getattr(result,"screenshot_path",False)
dir_name = os.path.dirname(__file__) # 当前脚本根目录

#由于fiddler保存尽可能要使用绝对路径,若是使用相对路径会保存到安装目录下,这是咱们不但愿的
sessiong_path = dir_name + "/" + "Error_session"#默认session保存路径
if not screenshot_path: screenshot_path=self.__screenshot_path else: if os.path.dirname(screenshot_path):#若是是绝对路径 sessiong_path=os.path.dirname(screenshot_path)+"/Error_session"#拿到运行test的根目录+FiddlerSessions result.startTest(self) testMethod = getattr(self, self._testMethodName) if (getattr(self.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False)): # If the class or method was skipped. try: skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or getattr(testMethod, '__unittest_skip_why__', '')) self._addSkip(result, skip_why) finally: result.stopTest(self) return try: success = False try: self.setUp() except SkipTest as e: self._addSkip(result, str(e)) except KeyboardInterrupt: raise except: result.addError(self, sys.exc_info())#启动setUp失败直接判断出错 else: try: testMethod() except KeyboardInterrupt: raise except (self.failureException,exceptions.WebDriverException):#若是是断言错误或WebDriverException,类型为fail,且增长截图 #增长截图 browser=self.getbrowser()#尝试拿浏览器实例 if browser: filename=self.__class__.__name__+"_"+self._testMethodName+".png"#格式:类名+方面名称 browser.get_screenshot_as_file(screenshot_path+"\\"+filename) reback_filename=filename else: reback_filename=None #保存sessions数据 sessionfile_name=self.__class__.__name__+"_"+self._testMethodName+"_err.saz"#注意格式是saz os.popen(dir_name+"/"+"Tools/D_interface.exe "+sessiong_path+"\\"+sessionfile_name)#控制台运行D_interface.exe由AutoIt生成,保存出错的session result.addFailure(self, sys.exc_info(),reback_filename)#回传截图名称给report,以便能显示在报告中 except SkipTest as e:#若是为跳过的异常,类型为Skip异常
......

 

最后个人demo文档结构大概是这样的:

 

 

其中Error_session是保存错误的session;FireFox_profile是咱们说到的火狐证书插件;Tools是咱们转换的3个简单的exe程序;screen_shot存放错误截图;IqunxingTest.py是咱们改写的unnitest类,咱们新建测试demo脚本:

#coding=utf-8
import IqunxingTest
import HTMLTestRunner
import sys,os
import unittest
from  selenium import webdriver
from  selenium.webdriver.firefox import firefox_profile
import time
dir_name = os.path.dirname(__file__)  # 拿到根目录
class Mydemo(IqunxingTest.IqunxingTest):
    u'''测试CSDN登陆'''
    @classmethod
    def setUpClass(cls):
        profile=firefox_profile.FirefoxProfile(profile_directory=dir_name+"/FireFox_profile")
        profile.set_preference("network.proxy.type", 5)#将浏览器代理设置为系统代理
        cls.browser=webdriver.Firefox(firefox_profile=profile)#启动带插件的浏览器
        cls.browser.implicitly_wait(10)
    @unittest.Myskip
    def test1(self):
        u'''打开csdn'''
        browser=self.browser
        browser.get("http://www.csdn.net/")
    @unittest.Myskip
    def test2(self):
        u'''csdn登陆'''
        os.popen(dir_name+"/"+"Tools/C_interface.exe")#在test开始前,清空Fiddler session信息
        browser = self.browser
        browser.find_element_by_link_text(u"登陆").click()
        time.sleep(1)
        browser.find_element_by_id("username").send_keys("test")
        time.sleep(1)
        browser.find_element_by_id("password").send_keys("test")
        time.sleep(1)
        browser.find_element_by_class_name("logging").click()#点击登陆
        if not browser.find_element_by_class_name("phr_first").is_displayed():#若是没有登陆成功是找不到这个控件会报错
            self.assertTrue(False,"login failed")
if __name__ == '__main__':
    fiddler_path = "C:\Program Files (x86)\Fiddler2\Fiddler.exe"#您的Fiddler路径
    s = os.popen(dir_name + "/Tools/S_interface.exe " + "\"" + fiddler_path + "\"")#启动Fidder
    module_name=os.path.basename(sys.argv[0]).split(".")[0]
    module=__import__(module_name)
    runner=HTMLTestRunner.HTMLTestRunner("reprot.html")
    all_suite=unittest.defaultTestLoader.loadTestsFromModule(module)
    runner.run(all_suite)
    os.popen('''taskkill /f /im "Fiddler.exe"''')#测试完成后关闭fiddler

由于咱们要找一些有用的sesssion信息,可喜的是Fiddler能过滤一些你设置完的信息,个人过滤信息以下:

由于测试CSDN,因此我只展现域名为*.csdn.net的会话;另外,一些css,js,png等无用信息我也隐藏了(正则表达式隐藏)。好了万事具有,咱们运行下这个demo:最后在Error_session下保存了咱们test2操做的全部http信息文件名为:Mydemo_test2_err.saz(过滤的除外),同时在screen_shot下保存错误的截图。咱们直接用fiddler打开这个saz文件:

 

从上面的截图能够看出来,咱们保存的session是完整的(过滤的除外)。并且咱们看到了咱们点击登陆时,使用的接口以及传递的相关信息。固然,咱们点击Fiddler其余标签事能获取一切咱们想获取的信息。

这一节个人思路说完了...固然,你可能用不上这些,可是你至少了解到了AutoIt以及selenium的一些知识!仍是那句:若是认为我说的有些道理,个人辛苦是值得的(毕竟写了一天);若是认为没用,请一笑而过~~

下个话题:selenium相关应用!!