zoukankan      html  css  js  c++  java
  • UI自动化实战进阶PO设计模式

    前言

    经过前面的实战我们已经编写了几个测试用例,下面我们要用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的执行顺序,具体知识不在此文章范围内,需要了解的自行百度。

  • 相关阅读:
    linux 使用crontab定时任务+shell脚本删除tomcat日志elasticsearch日志索引
    【转】Java8 Stream 流详解
    Mongo查询list数组中一个字段大于特定条数
    安装Elasticsearch出现 node validation exception 的问题处理
    mockito使用教程
    rocketmq4.4配置日志路径等级
    使用ResponseBodyAdvice统一包装响应返回String的时候出现java.lang.ClassCastException: com.xxx.dto.common.ResponseResult cannot be cast to java.lang.String
    服务器22端口连接超时 ssh: connect to host *** port 22: Operation timed out
    【JAVA基础】24 递归练习
    【JAVA基础】23 IO流-其他流
  • 原文地址:https://www.cnblogs.com/huny/p/14360219.html
Copyright © 2011-2022 走看看