Selenium自动化测试工具的介绍与使用

2023-11-18

Selenium自动化测试

什么是自动化测试

自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行
结果。总的概括即:将人为驱动的测试行为转化为机器执行的过程。
 
进入今天的主角:selenium 学习功能测试自动化首选工具就是selenium,它是一个web自动化测试工具。

selenium的特点

  • 支持多平台:IE、Chrome、Firefox、edge、Safari
  • 支持多语言:Python、C、Java、C#、ruby、js
  • 免费小巧,支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器执行,相当于分发机的功能。

自动化工具和自动化框架的区别

在学习selenium的过程中,我们写测试脚本需要使用unittest框架,这个框架提供的类或函数可以实现我们想要实现的测试功能。自动化测试框架一般可以分为两个层次,上层是管理整个自动化测试的开发,执行以及维护,在比较庞大的项目中,它体现重要的作用,它可以管理整个自动测试,包括自动化测试用例执行的次序、测试脚本的维护、以及集中管理测试用例、测试报告和测试任务等。下层主要是测试脚本的开发,充分的使用相关的测试工具,构建测试驱动,并完成测试业务逻辑。

自动化工具本片博客主要说的是selenium,除此之外,还有QTPRational Robot jmeterappiumsoapuiLoadrunner等等,以后有时间再去学习一下。工具主要的特点就是每个工具都有自己独特的操作界面供用户使用,selenium中的Selenium IDE就是自动化测试工具。

 

自动化测试适合适用什么项目,适合在什么时机做这种自动化合适?

自动化测试分为UI自动化测试和接口自动化测试。

UI自动化测试:适用于界面比较稳定的项目,前端开发完成之后,并且项目功能稳定。UI界面测试适合进行回归测试以及兼容性测试。

接口自动化测试:适用于后端开发完成,并且项目功能稳定;后端完成之后,就可以进行接口测试。做接口自动化的工具:soupUI、jmeter。

实施自动化测试的前提条件:需求变动不频繁、项目周期长、自动化测试脚本可以重复使用。

自动化适合的项目:产品型项目、机械并频繁的测试(比如兼容性测试)

 自动化测试的优势

降低大型系统的由于变更或者多期开发引起的大量的回归测试的人力投入,这可能是自动化测试最主要的任务,特
别是在程序修改比较频繁时,效果是非常明显的,自动化测试前期人力投入较多,但后期进入维护期后,可节省大
量人力,而手工测试后期需要增加大量人力用于回归测试。

  • 减少重复测试的时间,实现快速回归测试
  • 创建优良可靠的测试过程,减少人为错误
  • 可以运行更多更繁琐的测试
  • 可以执行一些手工测试困难或不可能进行的测试
  • 更好的利用资源
  • 测试具有一致性和重复性
  • 测试脚本的重用性

 

selenium的实现原理

Selenium主要有三个版本,分别是Selenium1.0,Selenium2.0,Selenium3.0

Selenium1.0:包括selenium IDE、selenium RC、selenium Grid(支持分布式)。Selenium1.0核心是selenium RC,所以Selenium1.0又称为Selenium RC,它是在浏览器中运用JavaScript应用,即用JavaScript代码获取页面上的任何元素并执行各种操作

Selenium2.0:核心是WebDriver,Selenium+WebDriver

WebDriver的原理:

(1)启动web浏览器,后台会同时启动基于Webdriver Wire协议的Web服务器作为selenium的远程服务器,并将其与浏览器绑定。绑定完成之后,服务器就开始监听客户端的操作请求。

(2)执行测试时,测试用例会作为客户端,将需要执行的页面操作请求以HTTP请求的方式发送给远程服务器。该HTTP请求的正文以Webdriver Wire协议规定的JSON格式来描述需要浏览器执行的具体操作。

(3)远程服务器接收到请求后,会对请求进行解析,并将解析结果发送给Webdriver,由Webdriver实际执行浏览器的操作。

(4)Webdriver可以看作是直接操作浏览器的原生组件,所以搭建测试环境时,通常需要先下载浏览器对应的WebDriver。

业界有一个形象的比喻来理解:乘客和出租车的例子。乘客就是客户端编写的测试脚本,司机就是我们的WedDriver,而出租车就是浏览器。

Selenium3.0:增加了edge 、Safari的原生驱动

学习Selenium主要就是学习WebDriver常用的API

常见API详解 

 #防止乱码
#coding = utf-8
#想使用selenium的webdriver里面的函数,首先需要把包导进来
from selenium import webdriver
import time
#我们需要操作的浏览器,这里使用的是谷歌浏览器,也可以是IE、Firefox
driver =webdriver.Chrome()
#访问百度首页
driver.get('http://www.baidu.com')
 #停3秒钟
time.sleep(3)
#百度输入框的id为kw,我们需要在输入框中输入Selenium,用send_keys进行输入
driver.find_element_by_id("kw").send_keys("Selenium")
time.sleep(3)
#百度搜索框按钮id叫su,找到后调用click函数模拟点击操作
#和click有相同效果的是submit(),都可以用来点击按钮,submit主要是用于提交表单
driver.find_element_by_id("su").click()
time.sleep(3)
#退出并关闭窗口的每一个相关的驱动程序
driver.quit()

注意:关闭窗口主要有两种方法,分别是close和quit。close是关闭当前浏览器窗口,quit不仅关闭窗口,还会彻底的退出webdriver,释放driver server之间的连接,所以quit的关闭比close更彻底,它会更好的释放资源。 

元素的定位 

对页面元素进行定位是自动化测试的核心,我们要想操作一个对象,首先应该识别这个对象。在一个页面中,每个对象属性是唯一的我们需要用一系列的方式去定位我们要操作的对象。WebDriver提供了以下几种方法定位元素:

  • id
  • name
  • class name
  • link text
  • partial link text
  • tag name
  • xpath
  • css selector

下面我将对每种定位方法进行举例。

#coding = utf-8
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
######################百度输入框的定位方式#################
#通过id定位
driver.find_element_by_id("kw").send_keys("selenium")
#通过name定位
driver.find_element_by_name("wd").send_keys(u"CSDN博客") #u表示以utf-8的格式输入
#通过tag name定位
driver.find_element_by_tag_name("input").send_keys("Python") #不能成功,因为input在这个页面有多个不唯一,无法定位到底是哪一个
#通过class name定位
driver.find_element_by_class_name("s_ipt").send_keys("Java")
#通过CSS定位
driver.find_element_by_css_selector("#kw").send_keys("C++")
#通过xpath定位
driver.find_element_by_xpath("//*[@id=kw]").send_keys(u"C语言")
#通过link test定位
driver.find_element_by_link_text("hao123").click()
#通过partial link test定位
driver.find_element_by_partial_link_text("hao").click()
driver.find_element_by_id("su").click()
time.sleep(3)
driver.quit()

智能等待

前面说过等待可以引入time包,从而在脚本中自由的添加休眠时间 。但是有时候我们不想等待一个固定的时间,于是可以通过implicitly_wait()方法方便的实现智能等待,它在一个时间范围内智能等待。

selenium.webdriver.remote.webdriver.implicitly_wait(time_to_wait)隐式等待一个元素被发现或一个命令完成,这个方法每次会话只需要调用一次time_to_wait

具体用法:

# coding = utf-8
from selenium import webdriver
import time #调入time 函数
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
browser.implicitly_wait(30) #智能等待30秒
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
browser.quit()

 打印信息

使用print打印title和URL

#coding = utf-8
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
print driver.title # 把页面title 打印出来
print driver.current_url #打印url
driver.quit()

浏览器的操作

浏览器的最大化

调用启动的浏览器不是全屏的,这样虽然不影响脚本的执行,但有时会影响我们观看脚本执行的变化。使用maximize_window()

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
print "浏览器最大化"
browser.maximize_window() #将浏览器最大化显示
time.sleep(2)
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
time.sleep(3)
browser.quit()

 设置浏览器的宽高

最大化不够灵活,所以我们可以随意的设置浏览页面的宽高,使用set_window_size(宽,高)

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
time.sleep(2)
#参数数字为像素点
print "设置浏览器宽480、高800显示"
browser.set_window_size(480, 800) 
time.sleep(3)
browser.quit()

浏览器的前进、后退

我们也可以实现浏览器的前进和后退

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
#访问百度首页
first_url= 'http://www.baidu.com'
print "now access %s" %(first_url)
browser.get(first_url)
time.sleep(2)
#访问新闻页面
second_url='http://news.baidu.com'
print "now access %s" %(second_url)
browser.get(second_url)
time.sleep(2)
#返回(后退)到百度首页
print "back to %s "%(first_url)
browser.back()
time.sleep(1)
#前进到新闻页
print "forward to %s"%(second_url)
browser.forward()
time.sleep(2)
browser.quit()

控制浏览器滚动条

#coding=utf-8
from selenium import webdriver
import time
#访问百度
driver=webdriver.Chrome()
driver.get("http://www.baidu.com")
#搜索
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(3)
#将页面滚动条拖到底部
js="var q=document.documentElement.scrollTop=10000"
driver.execute_script(js)
time.sleep(3)
#将滚动条移动到页面的顶部
js="var q=document.documentElement.scrollTop=0"
driver.execute_script(js)
time.sleep(3)
driver.quit()
#excute_script(script,*args),在当前窗口同步执行JavaScript

键盘事件

键盘键用法

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys #需要引入keys 包
import os,time
driver = webdriver.Chrome()
driver.get("http://demo.zentao.net/user-login-Lw==.html")
time.sleep(3)
driver.maximize_window() # 浏览器全屏显示
driver.find_element_by_id("account").clear()
time.sleep(3)
driver.find_element_by_id("account").send_keys("demo")
time.sleep(3)
#tab 的定位相当于清除了密码框的默认提示信息,等同上面的clear()
driver.find_element_by_id("account").send_keys(Keys.TAB)
time.sleep(3)
#通过定位密码框,enter(回车)来代替登陆按钮
driver.find_element_by_name("password").send_keys(Keys.ENTER)
time.sleep(3)
driver.quit()

键盘组合键用法

实现Ctrl+a,Ctrl+x

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
#输入框输入内容
driver.find_element_by_id("kw").send_keys("selenium")
time.sleep(3)
#ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
time.sleep(3)
#ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
time.sleep(3)
#输入框重新输入内容,搜索
driver.find_element_by_id("kw").send_keys("webdriver")
driver.find_element_by_id("su").click()
time.sleep(3)
driver.quit()

 鼠标事件

操作鼠标需要使用到ActionChains类

  • context_click()右击
  • double_click()双击
  • drag_and_drop()拖动
  • move_to_element()移动
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Chrome()
driver.get("http://news.baidu.com")
qqq =driver.find_element_by_xpath(".//*[@id='s_btn_wr']")
ActionChains(driver).context_click(qqq).perform() #右键
ActionChains(driver).double_click(qqq).perform() #双击
#定位元素的原位置
element = driver.find_element_by_id("s_btn_wr")
#定位元素要移动到的目标位置
target = driver.find_element_by_class_name("btn")
#执行元素的移动操作
ActionChains(driver).drag_and_drop(element, target).perform()
#ActionChains(driver)生成用户的行为。所有的行动都存储在actionchains 对象。通过perform()存储的行为。
#move_to_element(menu)移动鼠标到一个元素中,menu 上面已经定义了他所指向的哪一个元素
#perform()执行所有存储的行为

 定位一组元素

前面我们定位元素时,只定位了某一个特定的对象,但有时我们需要定位一组对象,这就需要使用find_elements方法

#coding=utf-8
from selenium import webdriver
import time
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\checkbox.html')
dr.get(file_path)
# 选择页面上所有的input,然后从中过滤出所有的checkbox 并勾选之
inputs = dr.find_elements_by_tag_name('input')
for input in inputs:
    if input.get_attribute('type') == 'checkbox':
        input.click()
time.sleep(2)
dr.quit()
#get_attribute获取属性值

多层框架/窗口定位

  • switch_to_frame()多层框架定位
  • switch_to_window() 多窗口定位

switch_to_frame()的功能是把当前定位的主体切换到frame里,即frame中实际上嵌入了另一个页面,而webdriver每次只能在一个页面识别,因此才需要用switch_to_frame方法去获取frame中嵌入的页面,对那个页面里的元素进行定位。

switch_to _default_content:从frame中嵌入的页面跳出,跳回到最外面的原始页面中。

#coding=utf-8
from selenium import webdriver
import time
import os
browser = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\frame.html')
browser.get(file_path)
browser.implicitly_wait(30)
#先找到到ifrome1(id = f1)
browser.switch_to_frame("f1")
#再找到其下面的ifrome2(id =f2)
browser.switch_to_frame("f2")
#下面就可以正常的操作元素了
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
time.sleep(3)
browser.quit()

 多层窗口定位:switch_to_window用法与switch_to_frame相同,如:driver.switch_to_window("windowname")

层级定位

对于这种定位的思路是:先点击显示出1个下拉菜单,然后再定位该下拉菜单所在的ul,再定位这个ul下的某个具体的link,如果要定位第一个下拉菜单中的Action选项:

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import time
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\level_locate.html')
dr.get(file_path)
#点击Link1链接(弹出下拉列表)
dr.find_element_by_link_text('Link1').click()
#找到id为dropdown1的父元素
WebDriverWait(dr,10).until(lambda the_driver:#10秒内每隔500毫秒扫描1次页面变化,当出现指定的元素结束
the_driver.find_element_by_id('dropdown1').is_displayed())#is_displayed()表示是否用户可见
#在父亲元件下找到link 为Action 的子元素
menu = dr.find_element_by_id('dropdown1').find_element_by_link_text('Action')
#鼠标定位到子元素上
webdriver.ActionChains(dr).move_to_element(menu).perform()
time.sleep(2)
dr.quit()

 下拉框处理

对于下拉框里的内容我们需要两次定位,先定位到下拉框,再定位到下拉框内的选项

#coding=utf-8
from selenium import webdriver
import os,time
driver= webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\drop_down.html')
driver.get(file_path)
time.sleep(2)
#先定位到下拉框
m=driver.find_element_by_id("ShippingMethod")
#再点击下拉框下的选项
m.find_element_by_xpath("//option[@value='10.69']").click()
time.sleep(3)
driver.quit()

 alert、confirm、prompt的处理

  • text返回alert、confirm、prompt中的文字信息
  • accept点击确认按钮
  • dismiss点击取消按钮

# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\alert.html')
dr.get(file_path)
# 点击链接弹出alert
dr.find_element_by_id('tooltip').click()
sleep(2)
alert = dr.switch_to.alert
alert.accept()
sleep(2)
dr.quit()

#coding:utf-8
from selenium import webdriver
from time import sleep
import os
driver=webdriver.Chrome()
driver.implicitly_wait(30)
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/send.html')
driver.get(file_path)
#点击“请点击”
driver.find_element_by_xpath("html/body/input").click()
#输入内容
driver.switch_to.alert.send_keys('webdriver')
driver.switch_to.alert.accept()
sleep(5)
driver.quit()

div对话框的处理

更多的时候我们在实际应用中碰到的并不是简单的警告框,而是提供更多功能的会话框

# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import os
import selenium.webdriver.support.ui as ui
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/modal.html')
dr.get(file_path)
# 打开对话框
dr.find_element_by_id('show_modal').click()
sleep(3)
# 点击对话框中的链接
link = dr.find_element_by_id('myModal').find_element_by_id('click')
link.click()
#dr.execute_script('$(arguments[0]).click()', link)
sleep(4)
# 关闭对话框
buttons =dr.find_element_by_class_name('modal-footer').find_elements_by_tag_name('button')
buttons[0].click()
sleep(2)
dr.quit()

上传文件操作

上传文件过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以selenium webdriver实现的方法是:只要定位上传按钮,通过send_keys添加本地文件路径就可以了,绝对路径和相对路径都可以,关键是上传的文件存在。

#coding=utf-8
from selenium import webdriver
import os,time
driver = webdriver.Chrome()
#脚本要与upload_file.html 同一目录
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/upload.html')
driver.get(file_path)
#定位上传按钮,添加本地文件
driver.find_element_by_name("file").send_keys('E:\\320S-15\\test\\baidu.py')
time.sleep(2)
driver.quit()

unittest框架解析

unittest是Python的单元测试,它提供了创建测试用例、测试套件以及批量执行的方案。

作为单元测试的框架,unittest也可以对程序最小模块的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须知道所使用语言的单元测试框架。利用单元测试框架,创建一个类,该类继承了unittest的TestCase,这样可以把每个case看成是一个最小的单元,有测试容器组织起来,到时候直接执行,同时引入测试报告。

unittest各组件的关系如图:

  • Test Fixture:测试固件,初始化和清理测试环境,比如创建临时的数据库、文件和目录等,其中setUp和tearDown是最常用的
  • TestCase:单元测试用例,是编写单元测试用例常用的类
  • TestSuite:单元测试用例的集合,TestSuite也是常用的类
  • TestRunner:执行单元测试
  • TestReport:生成测试报告

这里我们简单的创建一个测试用例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time
class Baidu1(unittest.TestCase):
    def setUp(self):
        print(u"setUp方法")
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com/"
        self.verificationErrors = []
        self.accept_next_alert = True
        #test fixture,清除环境
    def tearDown(self):
        print(u"tearDown方法")
        self.driver.quit()
        self.assertEqual([],self.verificationErrors)

    def test_hao(self):
         driver = self.driver
         driver.get(self.base_url + "/")
         driver.find_element_by_link_text("hao123").click()
         time.sleep(6)
         self.assertEqual(u"hao123_上网从这里开始", driver.title)
   # @unittest.skip("skipping")
    def test_baidusearch(self):
         driver = self.driver
         driver.get(self.base_url + "/")
         driver.find_element_by_id("kw").click()
         driver.find_element_by_id("kw").clear()
         driver.find_element_by_id("kw").send_keys(u"雪之城")
         driver.find_element_by_id("su").click()
    #判断element是否存在,可删除
    def is_element_present(self, how, what):
         try:
             self.driver.find_element(by=how, value=what)
         except NoSuchElementException as e:
             return False
         return True
     #关闭alert,可删除
    def is_alert_present(self):
        try: self.driver.switch_to.alert
        except NoAlertPresentException as e:
            return False
        return True

    def close_alert_and_get_its_text(self):
         try:
             alert = self.driver.switch_to.alert
             alert_text = alert.text
             if self.accept_next_alert:
                alert.accept()
             else:
                alert.dismiss()
             return alert_text
         finally: self.accept_next_alert = True

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

可以增加verbosity参数,例如unittest.main(verbosity=2)。在主函数中,直接调用main() ,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。这里的verbosity 是一个选项, 表示测试结果的信息复杂度,有三个值:

  • 0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个,失败20 成功80。
  • 1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ . ” 每个失败的用例前面有个“F”。
  • 2 ( 详细模式): 测试结果会显示每个测试用例的所有相关的信息。

批量执行脚本

构建测试套件

完整的单元测试需要执行很多个测试用例,开发人员通常需要编写多个测试用例才能对某一软件的功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在unittest中是用TestSuite类表示的。

如果我们编写了testbaidu1.py和testbaidu2.py两个文件,那我们怎么同时执行这两个文件呢?

# encoding: utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
class Baidu2(unittest.TestCase):
#test fixture,初始化环境
 def setUp(self):
     print(u"setUp方法")
     self.driver = webdriver.Chrome()
     self.driver.implicitly_wait(30)
     self.base_url = "http://www.baidu.com/"
     self.verificationErrors = []
     self.accept_next_alert = True
 #test fixture,清除环境
 def tearDown(self):
    print(u"tearDown方法")
    self.driver.quit()
    self.assertEqual([], self.verificationErrors)
 #测试用例,必须以test开头
 def test_baidusearch(self):
     driver = self.driver
     driver.get(self.base_url + "/")
     driver.find_element_by_id("kw").click()
     driver.find_element_by_id("kw").clear()
     driver.find_element_by_id("kw").send_keys(u"selenium")
     driver.find_element_by_id("su").click()
     driver.find_element_by_id("su").click()

 #判断element是否存在,可删除
 def is_element_present(self, how, what):
     try: self.driver.find_element(by=how, value=what)
     except NoSuchElementException as e:
        return False
     return True
 #判断alert是否存在,可删除
 def is_alert_present(self):
     try: self.driver.switch_to_alert()
     except NoAlertPresentException as e:
         return False
     return True
 #关闭alert,可删除
 def close_alert_and_get_its_text(self):
    try:
        alert = self.driver.switch_to.alert
        alert_text = alert.text
        if self.accept_next_alert:
            alert.accept()
        else:
            alert.dismiss()
        return alert_text
    finally: self.accept_next_alert = True


if __name__ == "__main__":
#执行用例
 unittest.main(verbosity=2)
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time
class Baidu1(unittest.TestCase):
    def setUp(self):
        print(u"setUp方法")
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com/"
        self.verificationErrors = []
        self.accept_next_alert = True
        #test fixture,清除环境
    def tearDown(self):
        print(u"tearDown方法")
        self.driver.quit()
        self.assertEqual([],self.verificationErrors)

    def test_hao(self):
         driver = self.driver
         driver.get(self.base_url + "/")
         driver.find_element_by_link_text("hao123").click()
         time.sleep(6)
         self.assertEqual(u"hao123_上网从这里开始", driver.title)
   # @unittest.skip("skipping")
    def test_baidusearch(self):
         driver = self.driver
         driver.get(self.base_url + "/")
         driver.find_element_by_id("kw").click()
         driver.find_element_by_id("kw").clear()
         driver.find_element_by_id("kw").send_keys(u"雪之城")
         driver.find_element_by_id("su").click()
    #判断element是否存在,可删除
    def is_element_present(self, how, what):
         try:
             self.driver.find_element(by=how, value=what)
         except NoSuchElementException as e:
             return False
         return True
     #关闭alert,可删除
    def is_alert_present(self):
        try: self.driver.switch_to.alert
        except NoAlertPresentException as e:
            return False
        return True

    def close_alert_and_get_its_text(self):
         try:
             alert = self.driver.switch_to.alert
             alert_text = alert.text
             if self.accept_next_alert:
                alert.accept()
             else:
                alert.dismiss()
             return alert_text
         finally: self.accept_next_alert = True

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

 这里就需要构建测试套件,构建测试套件有三种方法,不同的情景下用不同的方法:

  • 直接加入测试方法:addTest()
  • makeSuit()/TestLoader()
  • discover()

addTest()的应用

当有多个或几百个测试用例的时候,就需要一个测试容器(测试套件),把测试用例放在容器中进行执行,unittest模块中提供了addSuite类来生成测试套件,使用该类的构造函数可以生成一个测试套件的实例,该类提供了addTest把每个测试用例加入到测试套件中。

将testbaidu1.py,testbaidu2.py,runall.py放在同一级目录下,runall.py文件如下:

# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#导入testbaidu1,testbaidu2
import testbaidu1
import testbaidu2
#手工添加案例到套件,
def createsuite():
     suite = unittest.TestSuite()
     #将测试用例加入到测试容器(套件)中
     suite.addTest(testbaidu1.Baidu1("test_baidusearch"))
     suite.addTest(testbaidu1.Baidu1("test_hao"))
     suite.addTest(testbaidu2.Baidu2("test_baidusearch"))
     return suite

if __name__=="__main__":
    suite = createsuite()
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

上述做法有两个不方便的地方,阻碍脚本的快速执行,必须每次修改runall.py:
1)需要导入所有的py文件,比如import testbaidu1,每新增一个需要导入一个
2)addTest需要增加所有的testcase,如果一个py文件中有10个case,就需要增加10次

于是就引入了makeSuit()/TestLoader()


makeSuite()和TestLoader()


在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。TestLoader 用于创建类和模块的测试套件,一般的情况下TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。

修改runall.py

# -*- coding: utf-8 -*-
import unittest
import time
import testbaidu1
import testbaidu2
#手工添加案例到套件,
def createsuite():
    #suite = unittest.TestSuite()
    #将测试用例加入到测试容器(套件)中
    ######### makeSuite() ############### 
    # suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))
    # suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))
    # return suite
    ########## TestLoader() ##############
     suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)
     suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)
     suite = unittest.TestSuite([suite1, suite2])
     return suite

if __name__=="__main__":
     suite = createsuite()
     runner = unittest.TextTestRunner(verbosity=2)
     runner.run(suite)

经过makeSuite()和TestLoader()的引入,我们不用一个py文件测试类,只需要导入一次即可。
那么能不能测试类也不用每次添加指定呢?

discover()的应用

discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为discover(dir,pattern,top_level_dir=None)

__author__ = '320S-15'
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#手工添加案例到套件,
def createsuite():
    discover=unittest.defaultTestLoader.discover('../test',pattern='testbaidu*.py',top_level_dir=None)
    print discover
    return discover

if __name__=="__main__":
     suite=createsuite()
     runner = unittest.TextTestRunner(verbosity = 2)
     runner.run(suite)
     

unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0~9,A~Z,a~z 。所以, TestAdd 类会优先于TestBdd 类被发现, test_aaa() 方法会优先于test_ccc() 被执行。对于测试目录与测试文件来说, unittest 框架同样是按照这个规则来加载测试例。

如果想忽略某个用例的执行,可在方法前加@unittest.skip("skipping")

unittest断言

自动化测试中,对于每个单独的case来说,一个case执行结果中,必然会有期望结果与实际结果,来判断该case是通过还是失败。在unittest的库中提供了大量实用方法来检查预期值与实际值来验证case的结果。一般来说,检查条件大体分为等价性、逻辑比较以及其他,如果给定的断言通过,测试会继续执行到下一行的代码,如果断言失败,对应的case测试会立即停止或者生成错误信息(一般可以打印错误信息即可),但不要影响其他的case执行。

unittest的单元测试提供了标准的XUnit断言方法。下面是一些常见的断言:

断言方法 断言描述
assertEqual(arg1,arg2,msg=None) 验证arg1 = arg2,不等则fail
assertNotEqual(arg1,arg2,msg=None) 验证arg1 != arg2,不等则fail
assertTrue(exxpr,msg=None) 验证expr是true,如果false,则fail
assertFalse(exxpr,msg=None) 验证expr是false,如果true,则fail
assertIs(arg1, arg2, msg=None)  验证arg1、arg2是同一个对象,不是则fail
assertIsNot(arg1, arg2, msg=None)  验证arg1、arg2不是同一个对象,是则fail
assertIsNone(expr, msg=None)  验证expr是None,不是则fail
assertIsNotNone(expr, msg=None)  验证expr不是None,是则fail
assertIn(arg1, arg2, msg=None) 验证arg1是arg2的子串,不是则fail
assertNotIn(arg1, arg2, msg=None) 验证arg1不是arg2的子串,是则fail
assertIsInstance(obj, cls, msg=None)  验证obj是cls的实例,不是则fail
 assertNotIsInstance(obj, cls, msg=None) 验证obj不是cls的实例,是则fail

如前面的例子中:self.assertEqual(u"hao123_上网从这里开始", driver.title)

HTML报告生成

执行完脚本之后,还需要看到HTML报告,可以通过HTMLTestRunner.py生成测试报告。因为我本机安装的是Python2.7,所以需要下载HTMLTestRunner.py文件,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html。下载后将其放在testcase目录下或放入安装路径...\Python2.7\Lib目录下。

将上面的runall.py改成:

# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
import HTMLTestRunner
#手工添加案例到套件,
def createsuite():
    discover=unittest.defaultTestLoader.discover('../test',pattern='testbaidu*.py',top_level_dir=None)
    print discover
    return discover
if __name__=="__main__":
    curpath=sys.path[0]
    print(sys.path)
    print("=================")
    print(sys.path[0])
    #取当前时间
    now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))
    if not os.path.exists(curpath+'/resultreport'):
        os.makedirs(curpath+'/resultreport')
    print("=================")
    print(time.time())
    print("=================")
    print(time.localtime(time.time()))
    print("=================")
    print(now)
    #在当前目录下创建一个子目录resultreport,然后在该子目录下生成一个resultreport.html文件
    filename=curpath+'/resultreport/'+now+'resultreport.html'
    with open(filename,'wb') as fp:
         #生成html报告
         runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'测试报告',description=u'用例执行情况',verbosity=2)
         suite=createsuite()
         runner.run(suite)

测试报告用浏览器打开如图所示:

异常捕捉和错误截图

用例不可能每次都运行成功,也有运行不成功的时候,如果可以捕捉错误,这样就能更方便我们定位错误,因此unittest中有一个函数get_screenshot_as_file可以实现错误截图功能。

脚本实现实例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os
class Baidu1(unittest.TestCase):
    #test fixture,初始化环境
    def setUp(self):
     self.driver = webdriver.Chrome()
     self.driver.implicitly_wait(30)
     self.base_url = "http://www.baidu.com/"
     self.verificationErrors = []
     self.accept_next_alert = True

    #测试用例,必须以test开头
    def test_hao(self):
     driver = self.driver
     driver.get(self.base_url)
     driver.find_element_by_link_text("hao123").click()
     time.sleep(2)
     try:
        self.assertEqual(u'hao123_上网从这里开始', driver.title)
     except:
        self.savescreenshot(driver,'hao.png')

    #判断element是否存在,可删除
    def is_element_present(self, how, what):
     try: self.driver.find_element(by=how, value=what)
     except NoSuchElementException as e:
         return False
     return True

    #判断alert是否存在,可删除
    def is_alert_present(self):
     try: self.driver.switch_to.alert
     except NoAlertPresentException as e:
         return False
     return True
    #关闭alert,可删除
    def close_alert_and_get_its_text(self):
     try:
        alert = self.driver.switch_to.lert
        alert_text = alert.text
        if self.accept_next_alert:
            alert.accept()
        else:
            alert.dismiss()
        return alert_text
     finally: self.accept_next_alert = True
    #test fixture,清除环境
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)
    #截图
    def savescreenshot(self,driver,file_name):
        if not os.path.exists('./image'):#如果不存在image文件夹,我们就创建之
            os.makedirs('./image')
            now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
            print(time.time())
            print(time.localtime(time.time()))
            print(now)
            #截图保存
            driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
            time.sleep(1)

if __name__ == "__main__":
#执行用例
 unittest.main(verbosity=2)

如下就是捕捉到的错误截图:

数据驱动

如果需要多次执行一个案例,比如百度搜索,分别输入中文、英文、数字,或者同类型的多个数据,这时我们就想实现一次运行多个测试用例、unittest没有自带数据驱动功能,所以如果使用unittest,同时又要使用数据驱动,那么就可以使用ddt来完成。

需要安装ddt,可以在控制台输入如下命令:

pip install ddt

python setup.py install

ddt的使用方法

dd.ddt

装饰类,也就是继承自TestCase的类。

ddt.data:
装饰测试方法。参数是一系列的值。
ddt.file_data:
装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。
注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。
ddt.unpack:
传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理。

实现一个Testddt类,测试ddt.data

#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os,sys,csv
from ddt import ddt, data, unpack ,file_data
def getCsv(file_name):
     rows=[]
     path=sys.path[0].replace('\test','')
     print(path)
     with open(path+'/data/'+file_name,'rb') as f:
         readers=csv.reader(f,delimiter=',',quotechar='|')
         next(readers,None)
         for row in readers:
            temprows=[]
            for i in row:
                temprows.append(i.decode('gbk'))
            rows.append(temprows)
         return rows
#引入ddt
@ddt
class Testddt(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com"
        self.verificationErrors = []
        self.accept_next_alert = True

    @data(*getCsv('test_baidu_data.csv'))
    @unpack
    def test_hao(self,value,expected_value):
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(value)
        driver.find_element_by_id("su").click()
        time.sleep(2)
        print(value)
        self.assertEqual(expected_value, driver.title)
        print expected_value
        print driver.title
        #判断element是否存在,可删除
    def is_element_present(self, how, what):
         try: self.driver.find_element(by=how, value=what)
         except NoSuchElementException as e: return False
         return True
         #判断alert是否存在,可删除
    def is_alert_present(self):
        try: self.driver.switch_to.alert
        except NoAlertPresentException as e:return False
        return True
    #关闭alert,可删除
    def close_alert_and_get_its_text(self):
        try:
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            if self.accept_next_alert:
                alert.accept()
            else:
                alert.dismiss()
            return alert_text
        finally: self.accept_next_alert = True
    #test fixture,清除环境
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)
    def savescreenshot(self,driver,file_name):
        if not os.path.exists('./image'):
            os.makedirs('./image')
            now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
            #截图保存
            driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
            time.sleep(1)
if __name__ == "__main__":
#执行用例
    unittest.main(verbosity=2)

 test_baidu_data.csv:

data
周迅,周迅_百度搜索
林允,林允_百度搜索

测试ddt.file_data

#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os,sys,csv
from ddt import ddt, data, unpack ,file_data
@ddt
class Testddt(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com"
        self.verificationErrors = []
        self.accept_next_alert = True

    
    @file_data('test_baidu_data.json')
    def test_hao(self,value):
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(value)
        driver.find_element_by_id("su").click()
        time.sleep(2)
        print(value)
        # self.assertEqual(expected_value, driver.title)
        # print expected_value
        print driver.title
        #判断element是否存在,可删除
    def is_element_present(self, how, what):
         try: self.driver.find_element(by=how, value=what)
         except NoSuchElementException as e: return False
         return True
         #判断alert是否存在,可删除
    def is_alert_present(self):
        try: self.driver.switch_to.alert
        except NoAlertPresentException as e:return False
        return True
    #关闭alert,可删除
    def close_alert_and_get_its_text(self):
        try:
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            if self.accept_next_alert:
                alert.accept()
            else:
                alert.dismiss()
            return alert_text
        finally: self.accept_next_alert = True
    #test fixture,清除环境
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)
    def savescreenshot(self,driver,file_name):
        if not os.path.exists('./image'):
            os.makedirs('./image')
            now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
            #截图保存
            driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
            time.sleep(1)
if __name__ == "__main__":
#执行用例
    unittest.main(verbosity=2)

 test_baidu_data.json:

[
 "hello",
 "selenium"
]

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Selenium自动化测试工具的介绍与使用 的相关文章

  • Java基础——GUI——Swing中常用容器和组件

    1 swing中常用容器 1 JFrame 常用方法 1 构造方法 2 设置窗体可见 3 设置点击窗体的执行的操作 4 设置窗体的大小和位置 等价于上面两个方法 不管窗体多大 窗体运行起来都会出现在屏幕的中心 5 获取窗体容器 在容器中添加
  • 断点续传和多线程下载

    断点续传和多线程下载 HTTP是通过在Header里两个参数实现的 客户端发请求时对应的是Range 服务器端响应时对应的是Content Range Range 客户端发请求的范围 Content Range 服务端返回当前请求范围和文件
  • fadeOut、fadeIn

    p This is a paragraph p
  • 《Python 黑帽子》学习笔记 - 准备 - Day 1

    信息安全是一个有意思的方向 也是自己的爱好 从零开始 想在工作之余把这个爱好培养为自己的技术能力 而 web 安全相对来说容易入门些 于是选择 web 渗透测试作为学习的起点 并选择同样是容易入门的 Python 作为编程工具 潜心学习 持
  • 卡尔曼滤波算法 C语言实现 示例

    1 概念 卡尔曼滤波 Kalman filtering 是一种利用 k时刻 状态预测值 先验估计值 k 1时刻 状态最优估计值 后验估计值 k时刻 状态预测协方差 先验预测协方差 真实值与预测值之间的协方差 k时刻 状态最优估计协方差 后验
  • C# 字符串去掉括号和括号里面的内容

    using System Text RegularExpressions var majorname 考古学 清华大学 Replace Replace

随机推荐

  • 快速排序和归并排序的相同点和不同点(JAVA)

    首先我们贴出来快速排序的代码 public class QuickSort public int QuickSort int a int left int right int temp a left while left lt right
  • 小程序(十六)消息功能

    文章目录 一 数据库设计 二 系统消息的发送与收取设计 三 业务设计 四 消息实体设计 五 SpringBoot异步任务 1 开启异步注解功能 2 线程池创建 3 异步任务 发送消息 同步获取消息 4 controller 一 数据库设计
  • 字体样式的CSS表示大全

    https blog csdn net kellogg and nina article details 78349654 华文细黑 STHeiti Light STXihei 华文黑体 STHeiti 华文楷体 STKaiti 华文宋体
  • idea中创建xml,xml中路径报错

    1 先创建模板 2 新建xml 3 下面的路径可能会报红 4 解决方法
  • SpringFramework事件与监听机制(监听器)

    SpringBoot版本 2 0 2 RELEASE SpringFramework版本 RELEASE 文章目录 监听者从何而来 来自SpringBoot的监听器 来自SpringFramework的监听器 监听器监听的事件 监听器实现的
  • Linux搭建QT Creator环境

    虚拟机系统 VMware 14 操作系统 Ubuntu14 04 QT版本 4 8 7 1 下载QT 版本 QT4 8 7 路径 http download qt io archive qt 4 8 4 8 7 or https yunpa
  • 阿里云大数据专业认证(ACP级)学习笔记(精简) ...

    阿里云大数据专业认证 ACP级 学习笔记 精简 近百个视频精华考点总结 眼都快肿了 ODPS 流计算用的是ODPS ODPS的所有对象都隶属于项目空间 项目空间project 表table 表中的列支持Bigint长整 Double双精 S
  • Pytorch中tensor与numpy类型数据在GPU和CPU之间的转换

    1 CPU tensor转GPU tensor cpu imgs cuda 2 GPU tensor 转CPU tensor gpu imgs cpu 3 numpy转为CPU tensor torch from numpy imgs 4
  • 华为OD机试-查找充电设备组合-2022Q4 A卷-Py/Java/JS

    某个充电站 可提供n个充电设备 每个充电设备均有对应的输出功率 任意个充电设备组合的输出功率总和 均构成功率集合P的1个元素 功率集合P的最优元素 表示最接近充电站最大输出功率P max的元素 输入描述 输入为3行 第1行为充电设备个数n
  • 论文解读:Personalize Segment Anything Model with One Shot

    发表时间 2023 5 4 项目地址 https github com ZrrSkywalker Personalize SAM 体验地址 https huggingface co spaces justin zk Personalize
  • FFMPEG 合并视频文件(无损)

    FFMPEG 合并视频文件 无损 前言 最近在做视频转码相关业务 但是片源商给了一些DVD的零散视频片段 需要自己去合并片段并进行业务转码 本篇文章主要记录视频合并的过程及常见的合并方式 环境 FFMPEG BUILDS 4 4 1 FFm
  • [Pytorch系列-62]:生成对抗网络GAN - 基本原理 - 自动生成手写数字案例分析

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121914862 目录 第1章 基本原
  • 这三款软件让你轻松实现在线扫花识别植物

    如今 鲜花是我们日常生活中最常见的植物 但是随着鲜花种类的不断增多 它的许多的种类信息 想必大多数的朋友都难以认识清楚 因此 有的人就会使用一些识别鲜花的APP来帮助我们通过拍照而轻松获知鲜花的信息 那么你们知道识别鲜花的APP都有哪些吗
  • 小型中文版聊天机器人

    入门小菜鸟 希望像做笔记记录自己学的东西 也希望能帮助到同样入门的人 更希望大佬们帮忙纠错啦 侵权立删 目录 一 简单介绍与参考鸣谢 二 数据集介绍 三 数据预处理 1 重复标点符号表达 2 英文标点符号变为中文标点符号 3 繁体字转为简体
  • 【华为OD机试真题 Python语言】5、TLV解析

    文章目录 一 题目 题目描述 输入输出 样例1 二 思路参考 三 代码参考 作者 鲨鱼狼臧 个人博客首页 鲨鱼狼臧 专栏介绍 2023华为OD机试真题 使用Python进行解答 专栏每篇文章都包括真题 思路参考 代码分析 订阅有问题后续可与
  • Ansible 的脚本 --- playbook 剧本

    Ansible 的脚本 playbook 剧本 playbooks 本身由以下各部分组成 编写yaml文件示例 运行playbook 定义 引用变量 指定远程主机sudo切换用户 when条件判断 迭代 Templates 模块 1 先准备
  • 测试平台简介

    测试平台简介 一 被测系统介绍 被测系统为电商后台管理系统 功能模块包括 商品管理 订单管理 会员管理等 登录需要验证码 因没有后台代码 绕不开登录 只能手动获取到cookie 填充进测试用例 遇到真实项目 cookie这块逻辑需要再改造
  • Moonbeam与Nodle网络集成,增添物联网功能

    领先的波卡跨链互连开发平台Moonbeam近期宣布与Nodle Network达成XCM集成 将NODL Token带到Moonbeam生态之中 本次集成将会开启波卡中Moonbeam和Nodle网络以及通过Moonbeam互连合约相连的远
  • 如何在Swift开发中使用CocoaPods导入的第三方库

    今天在用swift写项目时 需要用CocoaPods引入SDWebImage这个三方库 于是开始在Vim命令中创建pod file 在创建之前需要cd到当前项目的目录中 Podfile创建步骤如下 1 创建Podfile touch Pod
  • Selenium自动化测试工具的介绍与使用

    Selenium自动化测试 什么是自动化测试 自动化测试指软件测试的自动化 在预设状态下运行应用程序或者系统 预设条件包括正常和异常 最后评估运行 结果 总的概括即 将人为驱动的测试行为转化为机器执行的过程 进入今天的主角 selenium