Web 自动化测试演练

以霍格沃兹学院的学员社区网页来演示如何把之前的 case 进行 PO 改造


Web 自动化测试演练_第1张图片

在这前的 case 中,我们打开了一个网址 (home.testing-studio.com),并搜索内容,然后断言结果是否符合预期,这是一个非常传统的 Web 自动化测试流程,接下来我们对其进行 PO 模式改造。

首先,我们需要创建两个对应的子包,一个叫作 page 用于存放 PO 定义;另一个叫作 testcase 用于存放调用的测试用例。创建完成之后和以前一样首先打开搜索内容,然后断言结果是否符合预期。

构思 PO

搜索结果页和首页一共是两个页面,所以我们需要创建两个 PO 。

首页的 PO 具有搜索功能,在搜索结果页中还可以根据用户名继续搜索,这里面又包含了一个独立的 PO。首页要有一个 PO ,这个 PO 是一个快捷搜索,我们搜索完关键字后会进入搜索结果页,也就是 search 结果后需要返回的是搜索结果页的 PO。

搜索结果页要允许用户可以根据特征进行搜索,比如根据作者搜索、根据分类搜索、或者根据一些其他条件进行搜索。

搜完之后,我们要对结果进行校验, 所以需要提供一些获取关键结果的方法。如果想获取搜索结果,可以使用 get_result 方法,如果要获取其中的一个作者,可以使用 get_authors 方法。

至此我们构思出了两个 PO,在这两个 PO 中包含四种方法。

编写 PO

基于之前的设计构思,我们开始编写我们的实现。

class Maindef search(self,keyword):
        return Search()

首先,我们回到 page 内新建一个 main 文件,表示主页。创建之后,新建一个 class 类,这个类里面有 search 功能。search 允许我们搜一个关键字,所以需要传参,我将参数命名为 keyword。 搜完 keyword 之后会返回搜索结果页,所以需要 return 一个对象,我们假设 return 一个 search,而此时 search 还没有实现,因此我们需要要创建它。

class Searchdef search_by(self,author=None):
        return self

现在我们开始创建 search。 因为 PO 是一个类,所以我们可以使用一个类的方法去创建它。


创建的类有几个方法,第 1 个方法叫 search_by,它可以根据一些特征来进行搜索。 搜索特征有哪些呢?根据我们刚才的界面你可以看到,除了搜索普通关键词之外还有很多选项,比如根据发帖人或分类进行搜索。

这次我根据作者进行搜索。首先我们要给作者一个默认值 None。搜索完成之后仍然是当前的结果页,注意,这个时候它也是 PO,只不过 return 的是它自己self。

class Search:
    def search_by(self,author=None):
        return self
    
    def get_result(self):
        return []
       
    def get_authors(self):
        return []

搜索完成后需要对结果进行判断,这时我们需要有 API 能够获取所有搜索结果中的作者。在这里,我们先获取搜索结果 get_results,搜索结果要 return 一个数组。然后是 get_authors,它要 return 一个字符串数组,用于我们后期的断言。

接着我们完善下类的导入,PO 模式基本上就已经成型了。

编写测试用例

我们开始编写测试用例。首先创建一个 test_search 的测试用例。

class TestSearch:
    def setup(self):
        self.search_page=Main().search("selenium")
    def test_search(self):
        authors=.self.search_page.search_by(author="Wayyt",keyword="selenium").get_authors
        assert len(authors)>0
        for result in authors:
            assert result =="Wayyt"

第一步是创建 Setup,Setup 中包含初始化内容,比如首页 Main page 初始化,首先需要导入 Main,初始化后调用快捷搜索方法,这里输入 selenium,搜索完之后才能进入结果页。在这里我们创建一个 search_page 变量,用于保存结果页,这样就完成了对搜索结果页的初始化。

接着是 test_search,我们这次搜索一个简单的功能,使用 self.search_page 的 search_by 功能去搜作者,也可以搜索其他内容,这里我们搜索 wayyt 这个作者。

当然 search_by 也支持搜索结果内容,所以这里还可以追加更多的参数,比如 keyword。

这里令 keyword 参数为 selenium 并根据作者进行搜索,所以我希望获取的结果是作者。获取结果后存储进来,并开始编写断言,比如说断言搜索的内容,第 1 个断言是 assert 获取的作者长度至少要大于 0。 接下来对其中每个作者进行断言, 令 assert 的内容等于我刚才所写的 wayyt,这样就编写出来了一个简单的测试用例,这个测试用例它很好地描述了我的业务:根据关键字进行搜索,搜完之后对结果进行比对。

我们运行一下 case,case 一开始是报错的,这属于正常情况,因为我们还没有做自动化。

从这一步开始,我们已经设计好整个业务的用例:case 构建没问题了,测试用例的构建和 PO 的模式构建已经非常顺畅了。

实现 PO

class Main:
    def __init__(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(5)
        self.wait = WebDriverWait(self.driver, 30)
        self.vars = {}
        self.driver.get("https://home.testing-studio.com/")
    def search(self, keyword):
        self.driver.find_element(By.CSS_SELECTOR,#search-button’).click()
        input_element = self.driver.find_element(By.CSS_SELECTOR,#search-term’)
        input_element.send_keys(“selenium”)
        input_element.send_keys(Keys.ENTER)

        return Search(self.driver)

Search类也需要用到driver来完成自己的自动化,所以我们给Search类增加一个可以传递driver的初始化方法。

class Search:
    def  __init__(self,driver):
        self.driver=driver

我们对Search类也进行改造,完成对应方法的实现

class Search:
    def __init__(self, driver):
        self.driver: WebDriver = driver
    def search_by(self, author=None, keyword=None):
        if keyword is not None:
            search_input=self.driver.find_element(By.CSS_SELECTOR,input.search-query’)
            search_input.clear()
            search_input.send_keys(keyword)
            # search_input.send_keys(Keys.ENTER)

        if author is not None:
            self.driver.find_element(By.NAME, ‘user-selector-renamed’).send_keys(author)
            self.driver.find_element(By.CSS_SELECTOR, ‘li .selected’).click()

        self.driver.find_element(By.CSS_SELECTOR, ‘button.search-cta’).click()
        return self

    def get_results(self):
        return [element.text for element in self.driver.find_elements(By.CSS_SELECTOR, ‘div.fps-topic’)]

    def get_authors(self):
        return [element.get_attribute(‘data-user-card’) for element in
                self.driver.find_elements(By.CSS_SELECTOR, 'div.fps-result .author a

接着运行用例,解决完一些细节问题后, case就可以运行通过了,你可以看到,我们搜 wayyt,搜出来他所发的几个帖子,然后每一个结果也都是符合我们的预期的。


通过这样一个小的案例,相信你已经知道基于 PO 的模式应该如何来写了。从这里也可以看到,就算没有做自动化测试,也可以把用例和场景提前写好,把流程连接起来,一旦连接完成,接下来就可以实现 自动化了,也就是我们可以提前设计好测试用例。


这就是整体的实现过程。 其实有了这些东西,基本的 PO 模式就实现了,但 PO 还有很多东西需要我们去继续改进。

继续改进的几个技术点

再说几个改进的方向,让你知道如何进行改进。


第 1 个技术点在于所有的oage类在功能上基本都是类似的,我们需要把它的公用功能抽象到一个统一的父类,比如 parent page 或者 base page 里,统一处理初始化与 driver 传递的问题,这个过程可以在 init 里面去进行解决和封装。Page 里面有大量的 driver.find 方法,我们在父类的Page中可以定义一个 find() 封装解决 driver.find 调用问题,这样可以减少大量的样板代码。


第 2 个技术点在于改进你的 find 策略,我们前面提到了,当广告\评价等各种弹框出现的时候,需要进行一些异常处理。当我们 find 原有控件时,会因为广告弹框导致 case 失败。我们希望这个 case 能够更智能,就需要额外去增加一个封装,这个封装是一个异常处理流程。我们可以使用 Python 的装饰器为一些通用 find 方法增加异常处理。


异常处理的逻辑是这样的,首先它会执行你的 find 方法,一旦发现报错就会进入异常处理流程,异常处理流程完成后会重新再次执行正常流程。通过这个方法,你的 case 就可以应对这种异常因素,从而提高稳定性。


第 3 个技术点是数据驱动,这个概念在后面的课时里面也会讲,所以在这儿我们就不详细说明了。


第 4 个技术点是 driver 驱动,当你有很多浏览器的时候,如何实现 driver 的合理安排和多浏览器的测试呢?这个时候我们需要创建一些 driver 的工厂方法,来便捷的去调用各个浏览器。


有了上面这些封装基础,小的框架改造就已经非常顺畅了,下一步我们需要对你的测试用例进行改造。测试用例很多通用的流程也是类似的。我们要把测试用例也抽象成一层,比如说 BaseTestCase,然后在这里面完成通用的一些 setup 和 teardown 的处理。


最后在测试报告中,我们可以使用 log 替换 print,生成的结果里面要支持 JUnit 风格的 XML 测试报告文件,这是pytest 也提供的功能,可以用持续集成系统结合。


然后还有一个是 Allure 框架,它也提供了一个非常漂亮的  HTML 报告,我们在前面的课程里面给你讲过,你要把它应用在你的框架里面,这样你的框架才会更易用,更易于维护,同时也能够更健壮,可以处理好各种异常处理流程。


你可能感兴趣的:(测试,前端,python,开发语言)