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

hcoder
hcoder
hcoder
订阅者
422
文章
0
评论
测试交流1 209字数 1468阅读4分53秒阅读模式

背景

背景之前写过了,在这儿:

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

目标

  1. 登录 玩技博客
  2. 发表一个新话题,并保存为草稿

备注

周末把 Selenium 的文档都过了一遍,说实话也不知道明天起床后,还记得住多少,所以写下来加强印象。文章源自玩技e族-https://www.playezu.com/185346.html

正文

学了几个感觉比较重要的概念:文章源自玩技e族-https://www.playezu.com/185346.html

  1. Wait
  2. Actions
  3. Domain specific language (DSL)
  4. Page object models (POM)

1、2 点没啥好说的,要用的时候查 api 就行了。
第 3、4 点我觉得挺关键的,后面可能要反复琢磨,贴下链接:文章源自玩技e族-https://www.playezu.com/185346.html

  • Selenium DSL
  • Selenium POM

意思是 Selenium 不推荐我第一篇中那种写法,那样很难维护,推荐按下列原则进行开发:文章源自玩技e族-https://www.playezu.com/185346.html

  1. DomainDrivenDesign: Express your tests in the language of the end-user of the app. (根据用户的行为定义你的用例)
  2. PageObjects: A simple abstraction of the UI of your web app. (把软件的 UI 抽象成一个页面)
  3. LoadableComponent: Modeling PageObjects as components. (把 PageObject 当作一个组件)
  4. BotStyleTests: Using a command-based approach to automating tests, rather than the object-based approach that PageObjects encourage. (建立一些通用的底层操作方法)

我瞎翻译的,大家看原文和原文里的代码,应该会有自己的理解。
然后我根据这些原则改了下代码,如下。文章源自玩技e族-https://www.playezu.com/185346.html

代码

贴代码前,先说一下组织架构:文章源自玩技e族-https://www.playezu.com/185346.html

+ BaseClass
+---+ 玩技博客SignInPage
+---+ TestHomeHomePage
+---+ 玩技博客TopicPage

base_class.py

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class BaseClass:
def __init__(self, driver: webdriver):
self.driver = driver
# self.driver = webdriver.Chrome()

def open_page(self, url):
self.driver.get(url)
def get_element(self, locator):
wait = WebDriverWait(self.driver, timeout=3)
return wait.until(EC.visibility_of_element_located(locator))
def click_element(self, locator):
self.get_element(locator).click()
def input_element(self, locator, text):
self.get_element(locator).send_keys(text)
def get_text(self, locator):
return self.get_element(locator).text

玩技博客_login_page.py

from selenium import webdriver
from selenium.webdriver.common.by import By
from pages.base_class import BaseClass
from pages.玩技博客_home_page import TestHomeHomePage
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
class 玩技博客SignInPage(BaseClass):
# url
    _URL = "http://玩技博客.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")
''' 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)
''' behaviors '''
def login_as(self, username: str, password: str):
self.pre_login()
.type_username(username) 
.type_password(password)
return self.submit_login()

玩技博客_home_page.py

from selenium.webdriver.common.by import By
from pages.base_class import BaseClass
from pages.testhome_topic_page import 玩技博客TopicPage
class TestHomeHomePage(BaseClass):
# locators
    _user_navbar_loc = (By.ID, "navbar-user-menu")
_menubar_loc = (By.ID, "navbar-new-menu")
_post_btn_loc = (By.XPATH, "//a[@href='http://testerhome.com/topics/new']")
''' actions '''
def exist(self):
return self.get_element(self._user_navbar_loc).is_displayed()
def post_new_topic(self):
self.click_element(self._menubar_loc)
self.click_element(self._post_btn_loc)
return self
''' behaviors '''
def post_topic(self):
self.post_new_topic()
return 玩技博客TopicPage(self.driver)

玩技博客_topic_page.py

from selenium.webdriver.common.by import By
from pages.base_class import BaseClass
class 玩技博客TopicPage(BaseClass):
# locators
    _title_input_loc = (By.ID, "topic_title")
_category_btn_loc = (By.ID, "node-selector-button")
_content_input_loc = (By.ID, "topic_body")
_draft_btn_loc = (By.ID, "save_as_draft")
_save_btn_loc = (By.CSS_SELECTOR, "input.btn.btn-primary")
_title_loc = (By.XPATH, "//h1[@class='media-heading']//span[@class='title']")
# locators inside category
    _selenium_category_loc = (By.XPATH, "//a[@data-id='73']")  # 要研究一下怎么把所有节点都获取到,然后根据title使用,不然我要写几十个locators

# flags
    _is_draft = True
''' actions '''
def write_title(self, title: str):
self.input_element(self._title_input_loc, title)
return self
def choose_category(self, category: str):
self.click_element(self._category_btn_loc)  # 要研究一下怎么把所有节点都获取到,然后根据title使用
        self.click_element(self._selenium_category_loc)
return self
def write_content(self, content: str):
self.input_element(self._content_input_loc, content)
return self
def save_as_draft(self):
self.click_element(self._draft_btn_loc)
return self
def submit_topic(self):
self.click_element(self._save_btn_loc)
return self
def get_title(self):
return self.get_text(self._title_loc)
''' behaviors '''
def post_topic(self, title: str, category: str, content: str, _is_draft=True):
self.write_title(title)
.choose_category(category)
.write_content(content)
.save_as_draft() if _is_draft else self.submit_topic()

测试

if __name__ == '__main__':
service = ChromeService(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
sign_in_page = 玩技博客SignInPage(driver)
home_page = sign_in_page.login_as("xxxx@foxmail.com", "xxxx")
assert home_page.exist()
topic_page = home_page.post_topic()
topic_testdata = {
"title": "记录用 selenium 把部分业务实现自动化测试(二)",
"category": "Selenium",
"content": "7月17日晴n今天好热,我多希望有朝一日能有人对我说:宝,早安。”,而不是”早,保安。“"
}
topic_page.post_topic(**topic_testdata)
assert topic_page.get_title() == topic_testdata["title"]
driver.quit()

效果图

(提交太多次话题被限制了,不上图了)文章源自玩技e族-https://www.playezu.com/185346.html

发现问题

遇到挺多问题的,有的解决了,有的没解决,都和大家分享一下:文章源自玩技e族-https://www.playezu.com/185346.html

  1. 在写话题的时候,要选"节点",这儿有很多个按钮,假如我要一个个写 locators,比较麻烦,后面再想下怎么拿到全部节点,然后根据 title 去定位。(应该可以用 find_elements)

    记录用 selenium 把部分业务实现自动化测试(二)插图
    文章源自玩技e族-https://www.playezu.com/185346.html
  2. 我的代码组织,体现不了 TopicPage 是 HomePage 下的一个子页面,后面再想下怎么写,可以让接盘侠一眼看出这两个 Page 的关系。文章源自玩技e族-https://www.playezu.com/185346.html

  3. Selenium 支持在已有页面下 debug,效率会高点,链接如下:Selenium Debug

  4. 写这个 locator 的时候遇到个小坑:

    _selenium_category_loc = (By.XPATH, "//a[@data-id='73']")
    

    原本我是用 By.CSS_SELECTOR 的,但会报 InvalidSelectorException 的错误,查了资料后发现是 css 中的类名不能以数字开头导致的。
    其实我按照文章里的方法也没成功,后面再查为啥了,先改用 XPATH,资料链接也贴下:
    关于 CSS_SELECTOR 的 InvalidSelectorException

计划

  1. 下周回公司后,尝试把手上的需求写成自动化
  2. 去 Github 上看下别人写的 Selenium 是怎样的
功能测试软件
 
    • 阿根
      阿根 9

      全部节点感觉可以整个方法遍历,如果是表结构,就遍历表格取 text 判断建议学习下Seldom & poium

    匿名

    发表评论

    匿名网友
    确定

    拖动滑块以完成验证