UI自動化實戰進階PO設計模式

Huny發表於2021-02-03

前言

經過前面的實戰我們已經編寫了幾個測試用例,下面我們要用PO設計模式來調整我們的程式碼,讓頁面元素和測試業務進行分離,這樣看起來直觀而且後期的維護也方便。
python有一個第三方的PO設計的庫,既然已經有了輪子,我們就可以直接造車了。

安裝

首先我們來安裝

pip install page_objects

Code

頁面封裝

#pages.py
from page_objects import PageElement, PageObject 

class Blog_Login_Page(PageObject):
    '''登陸頁面'''
    login_user = PageElement(id_ = 'user_login')
    login_passwd = PageElement(id_ = 'user_pass')
    login_jizhu = PageElement(id_ = 'rememberme')
    login_button = PageElement(id_ = 'wp-submit')

測試用例

#test_case.py
import unittest
from selenium import webdriver
from pages import Blog_Login_Page


username = passwd = '****'
url = 'http://139.199.192.100:8000/wp-login.php'


class Test_Blog(unittest.TestCase):
    '''部落格測試用例前置和後置'''

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get(url)
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()
        blog_home = Blog_Login_Page(self.driver)
        blog_home.login_user.send_keys(username)
        blog_home.login_passwd.send_keys(passwd)
        blog_home.login_jizhu.click()
        blog_home.login_button.click()

    def tearDown(self):
        self.driver.quit()


class Test_login(Test_Blog):
    '''部落格登陸測試用例'''

    def test_login_success(self):
        title_url = self.driver.current_url
        assert 'wp-admin' in title_url, '登陸不成功或者斷言錯誤'


if __name__ == '__main__':
    unittest.main()

下面我們在把之前的其他的用例按照這個模式進行整合
首先繼續進行封裝,如果後面的頁面越來越多,我們就必須封裝多個,並進行分類,首先建立一個pages的資料夾,然後將之前的封裝檔案改名成blog_login_page.py,然後建立新的封裝頁面檔案
這裡增加了目錄以及修改檔名稱,導包時可能會有點問題了,後面我的程式碼會有解決辦法

#blog_write_page.py
from page_objects import PageElement, PageObject ,MultiPageElement 

class Blog_Post_Page(PageObject):
    home_post = PageElement(css = '#menu-posts > a >.wp-menu-name')
    write_post = PageElement(css = '.page-title-action')
    write_post_alert = PageElement(css = 'div.components-modal__header > button > svg')
    write_post_title = PageElement(css = '#post-title-0')
    write_post_text = PageElement(css = '#post-content-0')
    write_post_release = PageElement(css = 'button.components-button.editor-post-publish-panel__toggle.editor-post-publish-button__button.is-primary')
    write_post_release_button = PageElement(css = 'div.editor-post-publish-panel__header-publish-button > button')
    post_release_status = PageElement(css = 'div.components-panel__body.post-publish-panel__postpublish-header.is-opened')

繼續封裝刪除部落格的頁面

#blog_delete_page.py
from page_objects import PageElement, PageObject ,MultiPageElement 


class Blog_Post_Page(PageObject):
    home_post = PageElement(css = '#menu-posts > a >.wp-menu-name')
    delect_post_locat = PageElement (css = 'td.author.column-author > a')
    delect_post_button = MultiPageElement (css = 'td.title.column-title.has-row-actions.column-primary.page-title > div.row-actions > span.trash > a')

頁面封裝好了,開始寫測試用例了,既然頁面有了分類,那麼我們的測試用例也應該進行分類
首先登陸用例

#test_login_blog.py
from pages.blog_login_page import Blog_Login_Page
import unittest
from selenium import webdriver

import os
import sys

path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)


username = passwd = '****'
url = 'http://139.199.192.100:8000/wp-login.php'


class Test_Blog(unittest.TestCase):
    '''部落格測試用例前置和後置'''

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get(url)
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()
        blog_home = Blog_Login_Page(self.driver)
        blog_home.login_user.send_keys(username)
        blog_home.login_passwd.send_keys(passwd)
        blog_home.login_jizhu.click()
        blog_home.login_button.click()

    def tearDown(self):
        self.driver.quit()


class Test_login(Test_Blog):
    '''部落格登陸測試用例'''

    def test_login_success(self):
        title_url = self.driver.current_url
        assert 'wp-admin' in title_url, '登陸不成功或者斷言錯誤'


if __name__ == '__main__':
    unittest.main()

寫文章用例

#test_write_blog.py
import unittest
import uuid
import os,sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)

from pages.blog_write_page import Blog_Post_Page
from test_login_blog import Test_Blog

uid = str(uuid.uuid1())
suid = ''.join(uid.split('-'))


class Test_write_blog(Test_Blog):
    '''寫部落格測試用例'''

    def test_write_blog_success(self):

        write_blog = Blog_Post_Page(self.driver)
        write_blog.home_post.click()
        write_blog.write_post.click()
        write_blog.write_post_alert.click()
        write_blog.write_post_title.send_keys(suid)
        write_blog.write_post_text.send_keys(suid)
        write_blog.write_post_release.click()
        write_blog.write_post_release_button.click()
        blog_status = write_blog.post_release_status
        assert '已被髮布' in blog_status.text, '文章未釋出或斷言錯誤'


if __name__ == '__main__':
    unittest.main()


刪除文章用例

#test_delete_blog.py
import unittest
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains
import os,sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)

from pages.blog_delete_page import Blog_Post_Page
from test_login_blog import Test_Blog


class Test_delete_blog(Test_Blog):
    '''刪除部落格測試用例'''

    def test_delete_blog_success(self):
        delete_blog = Blog_Post_Page(self.driver)
        delete_blog.home_post.click()
        mouse = delete_blog.delect_post_locat
        ActionChains(self.driver).move_to_element(mouse).perform()
        blog_title_old = delete_blog.delect_post_button
        bt = blog_title_old[0].text
        blog_title_old[0].click()
        blog_title_new = delete_blog.delect_post_button
        bt2 = blog_title_new[0].text
        assert bt != bt2, '文章未刪除成功'


if __name__ == '__main__':
    unittest.main()

這樣每個用例都是獨立的,當然我這裡呼叫了登陸用例的方法,這麼寫並不推薦,建議還是將登陸用例獨立出來,然後另外封裝一個成功登陸方法讓其他用例呼叫,具體方法我就不實現了,也算比較簡單了。
既然用例獨立了,如果我想一次執行多個用例呢?那麼我們需要新增一個測試套件,將需要執行的多個測試用例新增進來,如果用例過多新增比較麻煩怎麼辦?那就執行整個目錄下的用例,其中如果有不需要執行的可以使用skip進行跳過,這樣靈活的組合基本能滿足所有的場景了。
首先建立main目錄,再目錄中進行執行方法的分類

#run_test_class.py
import unittest
import os,sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)
print(path)
print(sys.path)

from test_case.test_login_blog import Test_login

if __name__ == '__main__':
    # 根據給定的測試類,獲取其中所有以test開頭的測試方法,並返回一個測試套件
    suite1 = unittest.TestLoader().loadTestsFromTestCase(Test_login)

    # 將多個測試類載入到測試套件中
    suite = unittest.TestSuite([suite1])

    # 設定verbosity = 2,可以列印出更詳細的執行資訊
    unittest.TextTestRunner(verbosity=2).run(suite)

這裡我遇到一個問題,導包的時候嘗試各種方法一直報錯,後面無意中執行發現再其他的目錄也存在相同的test_case目錄名稱並且也新增進了系統環境,所以一直查不到其他test_case目錄下有包.
這裡主要是命名不規範導致的,希望大家都能規範編碼,不然一個小問題可能會排查半天

修改目錄名稱並且和導包的名稱

#run_test_class.py
import unittest
import os,sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)
print(path)
print(sys.path)

from test_blog_case.test_login_blog import Test_login

if __name__ == '__main__':
    # 根據給定的測試類,獲取其中所有以test開頭的測試方法,並返回一個測試套件
    suite1 = unittest.TestLoader().loadTestsFromTestCase(Test_login)

    # 將多個測試類載入到測試套件中
    suite = unittest.TestSuite([suite1])

    # 設定verbosity = 2,可以列印出更詳細的執行資訊
    unittest.TextTestRunner(verbosity=2).run(suite)

執行成功,如果需要執行那幾個測試類直接匯入再新增到套件裡面進行執行即可

下面我們來寫執行整個目錄的方法

#run_test_discover.py
import os
import unittest

if __name__ == '__main__':
    path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'test_blog_case')
    suite = unittest.defaultTestLoader.discover(path, pattern='test*.py')
    runner = unittest.TextTestRunner()
    runner.run(suite)


再結合skip跳過用例的方法,再需要跳過的測試類或者測試方法新增下面的方法
@unittest.skip(reason):強制跳過,不需要判斷條件。reason是跳過原因的描述必須填寫。

#test_delete_blog.py
import unittest
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains
import os,sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)

from pages.blog_delete_page import Blog_Post_Page
from test_login_blog import Test_Blog

@unittest.skip('跳過刪除部落格用例')
class Test_delete_blog(Test_Blog):
    '''刪除部落格測試用例'''

    def test_delete_blog_success(self):
        delete_blog = Blog_Post_Page(self.driver)
        delete_blog.home_post.click()
        mouse = delete_blog.delect_post_locat
        ActionChains(self.driver).move_to_element(mouse).perform()
        blog_title_old = delete_blog.delect_post_button
        bt = blog_title_old[0].text
        blog_title_old[0].click()
        blog_title_new = delete_blog.delect_post_button
        bt2 = blog_title_new[0].text
        assert bt != bt2, '文章未刪除成功'


if __name__ == '__main__':
    unittest.main()

第一個執行的是刪除部落格用例並出現了s跳過的標記,為什麼先執行刪除部落格這裡需要了解unittest的執行順序,具體知識不在此文章範圍內,需要了解的自行百度。

相關文章