selenium unittest实战(2)-agileone项
关键词:selenium unittest python3 agileone
第一篇文章,给大家介绍了unittest编码规范,这篇我们主要分析下代码逻辑结构。
先回顾下python的目录结构:
一、登录模块 test_login.py
1)模块文件导入常用的库,比如Select,ActionChains等等
2)测试类继承unittest(unittest.TestCase),一个测试类的实例就是测试用例。需要写类说明,表明测试的模块。
image.png
3)初始化和清除
测试模块主要验证4个case。
I) 验证错误的用户名登录
II) 验证错误的密码登录
III) 验证不输入用户名登录
IV) 验证正常登录
初始化有两个方法:setUpClass和setUp。setUpClass是所有的测试用例执行前会初始化。setUp是每个测试用例前会初始化。
由于不想每个测试用例都打开浏览器,可以把初始化webdriver对象放到setUpClass。
image.png
清除也有两个方法:tearDownClass和tearDown。tearDownClass是所有的测试用例执行结束后清除。tearDown是每个用例执行结束后清除。所以当所有的测试用例测试完成后,关闭浏览器。
image.png
第一条用例:验证错误的用户名登录。测试完成后,如果要执行第二条用例,必须清除输入的用户名和密码,刷新浏览器即可清除。
image.pngimage.png
同时为了看到演示的效果,加一些延迟。
image.png
4)测试用例
setUpClass中写到"cls.driver=driver",相当于声明了一个类的属性,类属性是所有实例共享的,固可以直接self.driver调用webdriver对象。
后面的元素定位跟selenium一致,唯一特殊的是断言。根据断言的结果,unittest自动判断测试用例的结果。
常用的断言如下,其他的需要自行百度喽:
image.png
5)测试代码
使用 name == 'main',填写对应的测试代码。
unittest.main(verbosity=2)方法自动查找测试用例,加载执行。verbosity=2代表日志是详细级别。
driver=self.driver
代码如下:
import time
import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains
class Test_Login(unittest.TestCase):
'''登录模块'''
@classmethod
def setUpClass(cls):
driver=webdriver.Chrome(r"d:\webdriver\chromedriver.exe")
driver.implicitly_wait(10)
driver.get("http://localhost/agileone/index.php")
cls.driver=driver
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def setUp(self):
time.sleep(3) # 为了看到演示效果
def tearDown(self):
time.sleep(3) # 为了看到演示效果
self.driver.refresh()
def test_00_login(self):
'''验证错误的用户名登录'''
driver=self.driver
driver.find_element_by_id("username").send_keys("admin1")
driver.find_element_by_id("password").send_keys("admin")
driver.find_element_by_id("login").click()
time.sleep(1)
msg=driver.find_element_by_id("msg").text
self.assertIn("找不到该用户名",msg)
def test_01_login(self):
'''验证错误的密码登录'''
driver=self.driver
driver.find_element_by_id("username").send_keys("admin")
driver.find_element_by_id("password").send_keys("admin1")
driver.find_element_by_id("login").click()
time.sleep(1)
msg = driver.find_element_by_id("msg").text
self.assertIn("密码输入错误", msg)
def test_02_login(self):
'''验证不输入用户名登录'''
driver = self.driver
driver.find_element_by_id("login").click()
time.sleep(1)
msg = driver.find_element_by_id("msg").text
self.assertIn("用户名不能为空", msg)
def test_03_login(self):
'''验证正常登录'''
driver=self.driver
driver.find_element_by_id("username").send_keys("admin")
driver.find_element_by_id("password").send_keys("admin")
driver.find_element_by_id("login").click()
time.sleep(1)
logout=driver.find_element_by_css_selector("a[href*='logout']").text
self.assertEqual('注销',logout)
if __name__ == '__main__':
unittest.main(verbosity=2)
二、公告模块 test_notice.py
1)导入模块
与登录模块类似。
2)初始化和清除
setUpClass是所有用例测试之前进行初始化,所以完成登录的初始化即可。
tearDownClass所有用例测试完成之后,关闭浏览器。
image.png
公告管理模块主要验证4个用例:
I) 验证新增公告
II) 验证删除最新公告
III) 验证更新最新公告的标题
IV) 验证根据标题搜索公告。
为了保证用例与用例之间的独立性,测试之前和测试之后,都恢复到初始状态。
每个用例测试之前,都在登录后的首页状态,如下图。
每个用例准备开始测试,点击公告管理,进入模块,如下图。
测试之前.png
进入公告管理模块.png
初始化和清除设计如下:
image.png
3)验证新增公告
注意公告内容的输入需要先切换到frame里面,点击“新增”在主html。
image.png
4)验证删除最新公告
删除公告在新增之后执行,这个业务逻辑也保证了有公告可删。
万一没有公告可删时,直接断定此用例为失败状态。(self.fail('无公告,无法测试'))
image.png
5)验证更新最新公告的标题
先查找是否有最新的公告,如果没有直接断定为失败状态。如果有,点击最新公告的编辑,把原有的标题删掉,再输入"更新公告标题",最后再点击编辑。
image.png
6)验证根据标题搜索公告
搜索原有的公告不可控,所以设计为新增一个标题为test的公告。再根据这个test标题搜索。如果找到第一个搜索结果,即为成功。
image.png
7)测试代码
image.png
代码如下:
import time
import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains
class Test_Notice(unittest.TestCase):
'''公告模块'''
@classmethod
def setUpClass(cls):
#登录
driver=webdriver.Chrome(r"d:\webdriver\chromedriver.exe")
driver.implicitly_wait(10)
driver.get("http://localhost/agileone/index.php")
driver.find_element_by_id("username").send_keys("admin")
driver.find_element_by_id("password").send_keys("admin")
driver.find_element_by_id("login").click()
cls.driver=driver
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def setUp(self):
time.sleep(3)#为了看到演示效果
self.driver.find_element_by_css_selector("a[href*='notice']").click()#进入公告管理
def tearDown(self):
time.sleep(3)#为了看到演示效果
self.driver.find_element_by_css_selector(".header a[href='/agileone/index.php']").click()#回到登录首页
def test_00_addNotice(self):
'''验证新增公告'''
driver=self.driver
driver.find_element_by_id("headline").send_keys("测试标题")
driver.switch_to.frame(driver.find_element_by_css_selector(".ke-iframe"))
driver.find_element_by_tag_name("body").send_keys("测试内容")
driver.switch_to.default_content()
driver.find_element_by_id("add").click()
time.sleep(1)
self.assertIn("新增数据成功",driver.find_element_by_id("msg").text)
def test_01_delNotice(self):
'''验证删除最新公告'''
driver=self.driver
new_id=driver.find_elements_by_css_selector("#dataPanel tr[id*='dtrow']")
if new_id:
driver.find_element_by_css_selector("#dataPanel tr:nth-of-type(1) label:nth-of-type(2)").click()
driver.switch_to.alert.accept()
time.sleep(1)
self.assertIn("删除数据成功",driver.find_element_by_id("msg").text)
else:
self.fail("无公告,无法测试")
def test_02_updateNotice(self):
'''验证更新最新公告的标题'''
driver = self.driver
new_id = driver.find_elements_by_css_selector("#dataPanel tr[id*='dtrow']")
if new_id:
driver.find_element_by_css_selector("#dataPanel tr:nth-of-type(1) td:nth-last-of-type(1) label:nth-of-type(1)").click()
time.sleep(1)
headline=driver.find_element_by_id("headline")
headline.clear()
headline.send_keys("更新公告标题")
driver.find_element_by_id("edit").click()
time.sleep(1)
msg=driver.find_element_by_id("msg").text
self.assertIn("更新数据成功",msg)
else:
self.fail("无公告,无法更新")
def test_03_searchNotice(self):
'''验证根据标题搜索公告'''
driver = self.driver
#添加一个公告,再搜索
driver.find_element_by_id("headline").send_keys("test")
driver.switch_to.frame(driver.find_element_by_css_selector(".ke-iframe"))
driver.find_element_by_tag_name("body").send_keys("test")
driver.switch_to.default_content()
driver.find_element_by_id("add").click()
#输入标题搜索
driver.find_element_by_id("reset").click()
driver.find_element_by_id("headline").send_keys("test")
time.sleep(1)
driver.find_element_by_id("search").click()
time.sleep(1)
td=driver.find_element_by_css_selector("#dataPanel tr td:nth-of-type(2)").text
self.assertIn('test',td)
if __name__ == '__main__':
unittest.main(verbosity=2)
三、主程序 runner.py
为了生成HTML报告,需要导入HTMLTestRunner模块。
使用os.getcwd()获得项目的根目录,方便后续报告存入report目录。
使用defaultTestLoader实例的discover方法,自动发现测试用例。
使用time.strftime 把测试报告打上时间戳。
最后使用HTMLTestRunner实例运行所有用例。
import unittest
import time
import os
from HTMLTestRunner import HTMLTestRunner
#获取项目的根目录
test_dir = os.path.join(os.getcwd())
# 自动搜索项目根目录下的所有case,构造测试集;返回TestSuite对象
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
# 实例化TextTestRunner类
# runner = unittest.TextTestRunner(verbosity=2)
now = time.strftime('%Y-%m-%d %H_%M_%S') # 获取当前日期
filename = test_dir+ '\\report\\'+now + '_result.html' # 构造出完整的文件名路径
fp = open(filename, 'wb') # wb方式写入
runner = HTMLTestRunner(stream=fp, title='测试报告', description='agileone项目用例执行情况',verbosity=2) #构造runner
# 使用run()方法运行测试套件(即运行测试套件中的所有用例)
runner.run(discover)
四、测试报告
小结
1.所有的自动化都注意初始化和清除,保证用例与用例之间的独立性,不要让用例之间有过多的耦合。
2.UI自动化,很多地方需要加延迟,否则代码执行过快导致无法找到元素。