[技術博客]基於動態繼承類、WebDriver的瀏覽器兼容性測試框架搭建

問題背景

觀察使用selenium進行自動化測試的過程,咱們能夠將它概述爲:html

  • 啓動測試進程,在該進程中構建WebDriver
  • 啓動瀏覽器進程,將它與WebDriver創建鏈接
  • 使用WebDriver向瀏覽器發送請求從而達到控制的效果

在實際的代碼中,咱們所須要作的只有構建WebDriver和經過WebDriver提供的接口控制瀏覽器這兩件事情。對於後者,既然咱們的目標是各瀏覽器的兼容,那麼對於全部的瀏覽器,調用的WebDriver的接口都應該是一致的。因此這部分測試代碼應該被共享,惟一的區別應該是構建WebDriver的過程。python

咱們是基於django的測試框架,django的測試框架的本質是使用python的unittest。python的unittest是基於TestCase這個類的,也就是一個類一族測試樣例。上文中提到的兩件實際代碼中咱們作的事情就是以TestCase爲單位被編寫的。每當一個TestCase類被初始化時,咱們就爲它構建一個WebDriver,而後在這個類中的每一個測試樣例中經過WebDriver來控制瀏覽器。這就導出一個結論:一個類一個WebDriver、一套控制邏輯。django

注意到咱們但願控制邏輯在各瀏覽器的測試中是相同的,但各瀏覽器使用的WebDriver是不一樣的。瀏覽器

這就很天然地會想到類的繼承,一個基類寫控制邏輯,而後每一個瀏覽器派生這個基類,重載構建WebDriver的邏輯。但要注意到,咱們有大量的TestCase類,若是每一個TestCase都須要爲它編寫一族繼承類的代碼,並且這些代碼還幾乎如出一轍,僅僅只是由於代碼邏輯上須要因此才寫的話,那太累了。app

因此,咱們最後的需求是:有一族基類(控制邏輯),還有一族函數(構建WebDriver),若是將派生當作乘的話,咱們就是想要他兩的笛卡爾積。也就是每一個基類都有一族派生類,每一個派生類對應於給基類添加那一族函數中的一個函數。框架

核心概念:動態繼承

核心的概念是使用python類對象的思想在模塊被加載時進行動態繼承。這須要用到types module,python提供了該模塊使得動態構建類對象變得簡單。咱們只需簡單地調用types.new_class函數,並在參數中指定基類就能夠動態地從該基類中繼承一個新類。函數

在繼承獲得一個新類後咱們須要爲它添加它的繼承因子,也就是給它添加方法。這也很是簡單,在new_class的exec_body中指定就好了。新的方法會在new_class執行中被添加到被添加到新類的__dict__中,也就是命名空間中。測試

>>> import types
>>> def f(self):
    print("F")


>>> A = types.new_class("A", exec_body=lambda ns: ns.update({"nf":f}))
>>> a = A()
>>> a.nf()
F
>>> A.__dict__
mappingproxy({'nf': <function f at 0x00000227B0AD1C80>, 'module': 'types', 'dict': <attribute 'dict' of 'A' objects>, 'weakref': <attribute 'weakref' of 'A' objects>, 'doc': None})code

對於咱們的問題,也就只須要兩個循環,一個循環遍歷基類,一個循環遍歷繼承因子,而後在循環中構建新類便可。htm

基類遍歷問題

不過遍歷基類自己也是一個問題。迴歸咱們的原來的問題,咱們的基類是分佈在各文件中的大量TC(TestCase,下略),且這些TC之間自己就存在繼承結構。方便起見,咱們將基類的遍歷作成一個根據繼承關係的遞歸過程。使用全部TC的共同基類FrontBasicTC做爲遞歸的根,而後遞歸遍歷全部子類。

這裏又會涉及一個問題,上一章中提到了咱們會動態繼承獲得新類,這樣若是遞歸遍歷子類的話就會致使無窮遞歸問題了。因此咱們在動態繼承時要往新類的命名空間中加上一個標記,當在遞歸遍歷時發現這個標記就中止繼續對這個類進行遞歸。

子類識別問題

注意到咱們須要訪問FrontBasicTC的全部子類,咱們使用的是__subclasses__方法,若一個py文件沒有被執行過(也就是import過)的話,那麼該方法就不會識別到該py文件中的子類。因此咱們須要在test_factory.py的一開始就將全部基類都在的目錄template下的全部模塊都import進來。爲此咱們將template做爲一個包,而後在它的__init__.py文件中添加了__all__方法,用於from template import *

繼承因子遍歷問題

這原本不該該是個問題,但我爲了更無腦方便地開發,作了一些小trick。由於如今所使用的全部繼承因子都是類方法,因此我作成了讀入繼承因子們所在的模塊,而後遍歷該模塊的屬性,對於爲類方法的屬性,將其看作繼承因子進行操做。

這樣的好處是不用維護有哪些繼承因子的列表,簡單地加上原本就要加的@classmethod裝飾器就能夠自動被識別。

新類識別問題

如前文所述,咱們本質上使用的是python的unittest做爲測試框架。雖然咱們可以在一個模塊中動態繼承新類了,但還未將這些新類添加到這個模塊test_factory的命名空間中,這就致使python的UnitTestRunner識別不到這些新類。

爲此咱們在test_factory中將__dir__和__getattr__重載,前者返回這個模塊的屬性的名字的列表,後者根據名字獲取屬性對象。咱們用生成的新類做爲test_factory模塊的屬性,在__dir__和__getattr__中對應返回。

注:這裏的作法可能不太好,由於是針對UnitTestRunner所作的欺騙性工做,當python的unittest發生改變時可能會失效,並且還會致使沒法讀取test_factory的相似__name__這些元屬性。

相關文章
相關標籤/搜索