Selenium 记录用 selenium 把部分业务实现自动化测试(三)

冰冷热水壶 · 2022年07月26日 · 最后由 陈恒捷 回复于 2022年07月27日 · 6870 次阅读

背景

之前写过了,在这里:

目标

  1. 测试登录功能

备注

上周太忙,没啥进度,只是把 Unittest 的文档过了一遍,看下人家是怎么写用例的,链接我贴这儿了:

正文

代码

一个是登录页面的 Page Object,一个是登录功能的测试用例,当然我也没有需求文档,断言都是瞎写的。

testerhome_signin_page.py

from selenium.webdriver.common.by import By
from pages.base_class import BaseClass
from pages.testerhome_home_page import TestHomeHomePage


class TesterHomeSignInPage(BaseClass):
    # url
    _URL = "http://testerhome.com/"

    # locators
    _pre_login_btn_loc = (By.CSS_SELECTOR, "a.btn.btn-primary.btn-jumbotron.btn-lg")
    _account_input_loc = (By.ID, "user_login")
    _password_input_loc = (By.ID, "user_password")
    _login_btn_loc = (By.CSS_SELECTOR, "input.btn.btn-primary.btn-lg.btn-block")

    # message locators
    _error_mes_loc = (By.CSS_SELECTOR, ".alert.alert-warning")

    ''' actions '''

    def _pre_login(self):
        self._open_page(self._URL)
        self._click_element(self._pre_login_btn_loc)
        return self

    def _type_username(self, username: str):
        self._input_element(self._account_input_loc, username)
        return self

    def _type_password(self, password: str):
        self._input_element(self._password_input_loc, password)
        return self

    def _submit_login(self):
        self._click_element(self._login_btn_loc)
        return TestHomeHomePage(self.driver)

    def _get_error_message(self):
        return self._get_text(self._error_mes_loc, script=self._error_mes_loc[1])  # 这里不够智能,要优化

    ''' behaviors '''

    def login_as(self, username: str, password: str):
        self._pre_login() \
            ._type_username(username) \
            ._type_password(password)
        return self._submit_login()

    def login_without_account_password(self):
        self._pre_login() \
            ._click_element(self._login_btn_loc)
        return self._get_error_message()

    def login_as_wrong_account_password(self):
        self._pre_login() \
            ._type_username("xxxx") \
            ._type_password("xxxx") \
            ._click_element(self._login_btn_loc)
        return self._get_error_message()

    def login_as_missing_account_password(self):
        self._pre_login() \
            ._type_username("somebody") \
            ._click_element(self._login_btn_loc)
        return self._get_error_message()

    def repeat_login(self):
        self._pre_login() \
            ._type_username("somebody") \
            ._type_password("xxxx") \
            ._click_element(self._login_btn_loc)
        self._click_element(self._login_btn_loc)
        self._click_element(self._login_btn_loc)
        return self._get_error_message()

test_signin.py

import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager

from pages.testerhome_signin_page import TesterHomeSignInPage


class TestSignIn(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        service = ChromeService(executable_path=ChromeDriverManager().install())
        cls.driver = webdriver.Chrome(service=service)
        cls.sign_in_page = TesterHomeSignInPage(cls.driver)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_login_success(self):
        home_page = self.sign_in_page.login_as("abc@foxmail.com", "xxxx")
        assert home_page.exist()
        home_page.logout()

    def test_empty_account_password(self):
        error_message = self.sign_in_page.login_without_account_password()
        self.assertEqual(error_message, "继续操作前请注册或者登录。")

    def test_wrong_account_password(self):
        error_message = self.sign_in_page.login_as_wrong_account_password()
        self.assertEqual(error_message, "账号或密码错误,请重试")

    def test_missing_account_password(self):
        error_message = self.sign_in_page.login_as_missing_account_password()
        self.assertEqual(error_message, "账号或密码错误,请重试")

    def test_repeat_login(self):
        error_message = self.sign_in_page.repeat_login()
        self.assertEqual(error_message, "由于多次密码错误,您的账号已被暂时锁定,一小时后将自动解锁,或者你可以通过邮件手动解锁。")


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

效果图

发现问题

  1. 登录页面的结构,是这个样子的: 假如我直接取 element.text,会获取到所有子节点的 text,即会包含一个"x\n",造成断言失败: 网上查了一下,只找到注入 javascript 代码来实现,不太灵活,先记着,有空再优化。(不好意思,我忘了在哪儿看到的了,没链接) text = driver.execute_script(""" return document.querySelector(".alert.alert-warning").lastChild.nodeValue; """)
  2. 第二个问题,其实也不算是个问题,算是感想。 如备注所说,其实上周我自己没写多少,都看别人写的代码。 网友们推荐的网站看了,推荐的框架也看了,可惜我水平还不够,连说明文档里的示例也没看懂,还要继续努力才行。 周末写着写着代码,突然很迷茫,这个自动化到底是在写脚本,还是在做测试?我是不是明知这个功能是正常的,才去实现自动化? 于是我又把 Selenium 的文档看了一遍,里面有这么一段话,可能是一种答案,分享给大家。

Note that the tester still has not done anything but talk about unicorns in this code– no buttons, no locators, no browser controls. This method of modelling the application allows you to keep these test-level commands in place and unchanging, even if Larry decides next week that he no longer likes Ruby-on-Rails and decides to re-implement the entire site in the latest Haskell bindings with a Fortran front-end.

共收到 3 条回复 时间 点赞

在 selenium 里面用 js 的代码本来就是一件很曲线救国的事情,不管是延迟还是异常捕捉都很令人抓狂。。。

第二个问题,其实也不算是个问题,算是感想。 如备注所说,其实上周我自己没写多少,都看别人写的代码。 网友们推荐的网站看了,推荐的框架也看了,可惜我水平还不够,连说明文档里的示例也没看懂,还要继续努力才行。 周末写着写着代码,突然很迷茫,这个自动化到底是在写脚本,还是在做测试?我是不是明知这个功能是正常的,才去实现自动化? 于是我又把 Selenium 的文档看了一遍,里面有这么一段话,可能是一种答案,分享给大家。

看了下下面这段英文,大致是在说使用某种把应用模型化的方式,可以尽可能让你底层的测试命令在被测应用进行重构时,保持不变,好像和你说的这个问题不大对应?

自动化的核心价值,在于降低重复事情的执行成本,进而提高后续执行的效率。所以你现在做的事情,很明确就是在写脚本,不是在做测试。什么时候是做测试呢?答案就是执行脚本的时候。

至于是知道功能正常才去写自动化脚本,还是写完脚本去验证功能是否正常,这个就是开发模式上的差异了。大部分情况下,都是前者,先手工测试确认 OK 了,上线交付业务了,再补充自动化,提高后续回归测试的执行效率。而 ATDD、TDD 之类的测试驱动开发的模式,则是反过来,先写好脚本,然后不断改程序直到程序能通过脚本的测试。只是现在能把这个做好的不多(TDD 其实对每个模块做到低耦合要求挺高的),而且由于 UI 自动化本身的特性(需要有明确的界面控件及交互相关信息,才有办法写脚本,基于设计阶段的原型图和 UI 图基本没办法写,不像单测只要有设计阶段的函数定义就可以写),基本上没怎么见到过在 UI 自动化领域实现测试先行。

最后说一句,写脚本这门技能,是任何一个高效率的人都需要具备的能力,产品在 excel 写公式、运维写 shell 做批量处理,都是一种写脚本。不用太纠结它是不是测试,确认他能提升你的工作效率,让你具备更强的竞争力,这个就够了。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册