本章节我们再来说说测试,单元测试和功能测试。单元测试我们在数据验证章节简单提过了,本章我们进一步如何用单元测试来测试view的功能代码;同时,也涉及一下基于 selenium 的功能测试做法。笔者过去的项目上常规的功能测试都是由测试人员通过人工点击按钮的方式来完成的,这里我们利用 selenium 来完成,大家体会一下当功能测试可以回归的时候是啥赶脚。

1.1. 单元测试覆盖task_start函数

参考前面的单元测试例子,完成 test_task_start 任务下达的单元测试代码,断言状态是否变更为下达状态。

def test_task_start(self):"""测试任务下达."""#1构建模拟任务data={'TaskNum':100,'Source':'101','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,}task = Task(**data)#task.save()#2创建业务类对象,并调用任务分解函数taskBiz=TaskBiz()taskBiz.task_decompose(task,None)#①taskBiz.task_start(task)#②self.assertEqual(task.State,Task.STATE_RUNNING)

①:执行任务分解。

②:执行任务下达,断言任务下达是否满足状态控制要求。

通过 IDE 的快捷菜单进入到 cmd 命令行运行单元测试, VS IDE 的 Test Explore 笔者用下来不是非常好用,有的时候单元测试函数刷新不出来。

命令行单元测试执行效果

it’s ok!

1.2. selenium 功能测试

通过编写模仿用户操作的 Selenium 测试脚本,可以从终端用户的角度来测试应用程序,就像真实用户所做的一样。与通常的测试人员通过人工操作的方式,采用 Selenium 确实能够带来效率的大幅度提升,尤其新版本发布回归测试的时候!

1.2.1. python 环境安装 Selenium

键入 selenuim ,点击安装链接

1.2.2.安装 chromedriver 驱动

如果要使用WebDriver在Chrome浏览器上进行测试时,需要从安装chromedriver驱动程序。下载网址: http://chromedriver.storage.googleapis.com/index.html

笔者写这篇的本机环境如下:

下载对应版本的chromedriver驱动文件后,下载后把文件解压,然后放到本机chrome浏览器文件路径里即可,如:C:\Program Files (x86)\Google\Chrome\Application

1.3. 添加 functional_test.py 功能测试代码

先加入简单的测试脚本代码,打开客户端任务列表,判断当前浏览器窗口标题是否满足断言值 ——“任务清单”。

from  unittest import TestCase
import djangofrom selenium import webdriverclass FunctionalTest(TestCase):@classmethoddef setUpClass(cls): #①super(FunctionalTest, cls).setUpClass()django.setup()   cls.browser=webdriver.Chrome()cls.live_server_url = 'http://localhost:8001/task/'  @classmethoddef tearDownClass(cls): #②cls.browser.quit()def test_task_list(self): #③self.browser.get(self.live_server_url )self.browser.maximize_window()self.browser.implicitly_wait(3)#④#假定网页应该包含“任务列表”的标题self.assertIn('任务列表',self.browser.title)

①:单元测试类初始化函数,执行测试时,只初始化执行一次,我们把测试需要准备的一些数据放在这里初始化。

②:单元测试类销毁函数,执行测试时,只销毁执行一次

③:访问任务列表url,并断言窗口标题是否包含“任务列表”

④:使用隐式等待3秒钟,如果selenium 提前获得返回,会提前结束等待。

命令行运行测试:

D:\my tfs\IndDemo>python manage.py test Task.functional_test
System check identified no issues (0 silenced).
DevTools listening on ws://127.0.0.1:64721/devtools/browser/0c65b5e3-1000-4233-a746-30b9142532faF ====================================================================== FAIL: test_task_list (Task.functional_test.FunctionalTest)Traceback (most recent call last): File "D:\my tfs\IndDemo\Task\functional_test.py", line 26, in test_task_list self.assertIn('任务列表',self.browser.title) AssertionError: '任务列表' not found in ''Ran 1 test in 6.486s
FAILED (failures=1)
D:\my tfs\IndDemo>

这里我们演示一下测试驱动开发 里,小步的推进的原则,先添加代码满足这个测试条件,然后再运行测试,满足当前测试了,添加新的测试断言,再添加新的代码来满足这个测试断言。测试驱动的开发对于开发新手来说确实会带来很多好处,就是不用每次考虑够多功能点,积硅步而至千里。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head><meta charset="utf-8" /><title>任务列表</title>
</head>
<body>
...

模板文件:tasks.html

运行功能测试:

D:\my tfs\IndDemo>python manage.py test Task.functional_test
System check identified no issues (0 silenced).
DevTools listening on ws://127.0.0.1:65125/devtools/browser/94e6664d-cda5-41e1-adc7-b9820cb73241
.
----------------------------------------------------------------------
Ran 1 test in 5.677s
OK
D:\my tfs\IndDemo>

这次我们收获了一个 “ ok ” !

1.4.场景功能测试

本例客户端功能测试场景中,假定 WCS 客户端不能手动增加新任务,只能够查看任务列表和详情,和源地址和目标地址的修改操作,以及执行分解、下达操作等。下面最后完成功能测试代码:

from  unittest import TestCase
import django
from django.test import LiveServerTestCase
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from Task.TaskBiz import Taskclass FunctionalTest(LiveServerTestCase):@classmethoddef setUpClass(cls): super(FunctionalTest, cls).setUpClass()django.setup()   cls.browser=webdriver.Chrome()#cls.live_server_url = 'http://localhost:8001/task/'  #1初始化测试任务1data={'TaskNum':200,'Source':'101','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,}task = Task(**data)task.save()#2初始化测试任务2data={'TaskNum':201,'Source':'102','Target':'05-01-02','Barcode':'101001001009','State':1,'Priority':1,}task2 = Task(**data)task2.save()@classmethoddef tearDownClass(cls): #②cls.browser.quit()def test_task_list(self): #③#print(self.live_server_url + '/task/')self.browser.get(self.live_server_url + '/task/' )self.browser.maximize_window()self.browser.implicitly_wait(3)#④#假定网页应该包含“任务列表”的标题self.assertIn("任务列表",self.browser.title)#获取table并断言table row 里是否包含初始化的任务数据table = self.browser.find_element_by_id('id_task_table')#table = self.browser.find_elements_by_tag_name('table')rows = table.find_elements_by_tag_name('tr')#表标题行self.assertIn("任务号",rows[0].text)         #表第一行数据self.assertIn('200',rows[1].text)        self.assertIn('101001001008',rows[1].text)#表第二行数据self.assertIn('201',rows[2].text)#对第一个执行任务分解操作self.browser.find_element_by_id('1-decompose').click()self.browser.implicitly_wait(3)#sleep(3)table = self.browser.find_element_by_id('id_task_table')rows = table.find_elements_by_tag_name('tr')#表第一行数据包含子任务数 10self.assertIn('10',rows[1].text)        self.assertIn('处理成功',rows[1].text)#对第一个执行任务下达操作self.browser.find_element_by_id('1-start').click()self.browser.implicitly_wait(3)table = self.browser.find_element_by_id('id_task_table')rows = table.find_elements_by_tag_name('tr')#表第一行数据包含子任务数 10      self.assertIn('执行中',rows[1].text)#修改第二个任务self.browser.find_element_by_id('2-change').click()self.browser.implicitly_wait(3)self.browser.find_element_by_name('source').send_keys('111')self.browser.find_element_by_name('target').send_keys('05-01-11')self.browser.find_element_by_name('target').send_keys(Keys.ENTER)self.browser.implicitly_wait(3)table = self.browser.find_element_by_id('id_task_table')rows = table.find_elements_by_tag_name('tr')self.assertIn('111',rows[2].text)        self.assertIn('05-01-11',rows[2].text)

运行功能测试:

D:\my tfs\IndDemo>python manage.py test Task.functional_test Creating test database for alias 'default'... System check identified no issues (0 silenced). DevTools listening on ws://127.0.0.1:51015/devtools/browser/82f971f5-83ba-4b55-bc19-ff700e7aedb1 E---------------------------------------- ---------------------------------------- ====================================================================== ERROR: test_task_list (Task.functional_test.FunctionalTest)Traceback (most recent call last):File "D:\my tfs\IndDemo\Task\functional_test.py", line 59, in test_task_listself.browser.find_element_by_id('1-decompose').click()File "C:\Python\Python36-32\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_idreturn self.find_element(by=By.ID, value=id_)File "C:\Python\Python36-32\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element'value': value})['value']File "C:\Python\Python36-32\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in executeself.error_handler.check_response(response)File "C:\Python\Python36-32\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="1-decompose"]"}(Session info: chrome=90.0.4430.72)Ran 1 test in 8.513s
FAILED (errors=1)
Destroying test database for alias 'default'...
D:\my tfs\IndDemo>

代码“self.browser.find_element_by_id(‘1-decompose’).click()”这句代码找不到相应的id='1-decompose’的html element。 因此,需要改进一下模板代码如下:


{% for task in tasks %}

    <tr><td>{{task.TaskId }}</td><td>{{task.TaskNum}}</td><td>{{task.Source}}</td><td>{{task.Target}}</td><td>{{task.Barcode}}</td><td>{{task.get_State_display}}</td><td>{{task.get_Priority_display}}</td><td>-</td><td>-</td><td>{{task.job_set.count}}</td><td><a id="{{task.TaskId}}-decompose" href="{{task.TaskId }}/decompose/">分解</a> <a id="{{task.TaskId}}-start" href="{{task.TaskId }}/start/">下达</a> <a id="{{task.TaskId}}-change" href="{{task.TaskId }}/change/">修改</a></td></tr>{%endfor%}


相对于每行的操作链接赋值一个唯一的 id 值,重新运行功能测试:

D:\my tfs\IndDemo>python manage.py test Task.functional_test Creating test database for alias 'default'... System check identified no issues (0 silenced). DevTools listening on ws://127.0.0.1:51636/devtools/browser/89f5a372-fbe3-4ecd-b581-07d813d56c55 .Ran 1 test in 6.224s
OK
Destroying test database for alias 'default'...
D:\my tfs\IndDemo>

功能测试运行通过,接下来我们进一步完善单元测试。

1.5. 单元测试覆盖 view

Django test 也可以针对发布的url进行单元测试,从而覆盖url和view代码,下面我在Task/tests.py里增加 class TaskViewTest(TestCase) 类专门测试发布的url是否符合开发预期,测试代码如下:


class TaskViewTest(TestCase):
“”“Tests for the application views.”""

    # Django requires an explicit setup() when running tests in PTVS@classmethoddef setUpClass(cls):super(TaskURLTest, cls).setUpClass()django.setup()#1初始化测试任务1data={'TaskNum':200,'Source':'101','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,}task = Task(**data)task.save()#2初始化测试任务2data={'TaskNum':201,'Source':'102','Target':'05-01-02','Barcode':'101001001009','State':1,'Priority':1,}task2 = Task(**data)task2.save()def test_task_change(self):data={'source':'111','target':'05-01-11'}#更新第一个task的源和目标值response=self.client.post('/task/1/change/',data)model = Task.objects.get(pk=1)self.assertEqual(model.Source,'111')self.assertEqual(model.Target,'05-01-11')response=self.client.get('/task/')       self.assertIn('111',response.content.decode())self.assertTemplateUsed(response,'Task/tasks.html')def test_task_decompose(self):response=self.client.get('/task/1/decompose/')        model = Task.objects.get(pk=1)self.assertEqual(model.job_set.count(),10)def test_task_decompose(self):self.client.get('/task/1/decompose/')        model = Task.objects.get(pk=1)self.assertEqual(model.job_set.count(),10)self.client.get('/task/1/start/')        model = Task.objects.get(pk=1)self.assertEqual(model.State,Task.STATE_RUNNING)

目前,Task APP 单元测试覆盖了所有发布的url,项目迭代推进过程中,新的改动会不会导致bug,回归运行一把单元测试,如果“红”了一片,赶紧回滚改动的代码。

1.6.小结

django 的单元测试每次运行都是重新构建数据库和销毁数据库,所以不用担心测试数据重复或状态的问题,数据每次运行都是按照测试逻辑来执行的。尤其功能测试基于LiveServerTestCase时,这个特点简直“香”得不要不要的。传统人工操进行的功能测试每次数据准备都够忙一阵子的,这一点也是笔者使用django爽点之一。

资源分享

下面这些是我的收集和整理的资料,对于学习【软件测试】的朋友来说应该是最全的教程仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你

关注【程序媛木子】微信公众号里海量资源免费获取,技术交流群(644956177)

python工业互联网应用实战13—基于selenium的功能测试相关推荐

  1. 38. 实战:基于selenium的某宝秒杀抢购系统(附完整代码)

    目录 前言 目的 思路 代码实现 1. 自动打开浏览器,并配置选项 2. 实现扫码登陆 3. 进入购物车选择秒杀商品(本例勾选全选) 4. 获取当前时间,大于设定时间时下单 5. 下单成功后语音提示用 ...

  2. python自动输入账号密码_Python如何基于selenium实现自动登录博客园

    这篇文章主要介绍了Python如何基于selenium实现自动登录博客园,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需要做的准备: 本文章是使用 ...

  3. 工业互联网新发展:基于 HTML5 WebGL 的高炉炼铁厂可视化系统

    前言 在当今 工业4.0 新时代的推动下,不仅迎来了 工业互联网 的发展,还开启了 5G 时代 的新次源.而伴随着带宽的提升,网络信息飞速发展,能源管控上与实时预警在工业互联网中也占着举足轻重的地位, ...

  4. 工业互联网新发展:基于 HTML5 WebGL 的高炉炼铁厂可视化系统!

    前言 在当今 工业4.0 新时代的推动下,不仅迎来了 工业互联网 的发展,还开启了 5G 时代 的新次源.而伴随着带宽的提升,网络信息飞速发展,能源管控上与实时预警在工业互联网中也占着举足轻重的地位, ...

  5. 28. 实战:基于selenium实现12306自动购票

    目录 前言 目的 思路 代码实现 1. 进入登录界面,输入账号密码 2. 点击登录按钮,完成滑块验证 3. 在个人中心点击购票,跳转 4. 输入出发地.目的地,从控制台输入得到 5. 文本框输入出发日 ...

  6. python演唱会抢票脚本_基于selenium的大麦网演唱会抢票程序

    自从我在知乎分享了代码后,很多人关注了我的代码,自己也很荣幸,哈哈.但是好多人运行我的代码却出现了这样那样的问题,我刚开始也很纳闷,明明自己当初试了好几个演唱会都可以,为什么现在有这么多问题,上段时间 ...

  7. python携程酒店评论_Python基于selenium爬取携程酒店评论信息

    爬取站点 任意一个携程酒店的详细链接,这里给出了四个,准备开四个线程爬取: https://hotels.ctrip.com/hotel/6278770.html#ctm_ref=hod_hp_hot ...

  8. python模拟拖拽文件_python 基于selenium实现鼠标拖拽功能

    1.准备html文件 首先我们需要准备一个鼠标滑动的html文件,用来演示鼠标滑动的效果,注意需要将我们的html文件放在自己的服务器上, 这样我们才能够通过selenium来进行验证.html文件如 ...

  9. 测试床/基于“5G+工业互联网”的无忧智能工厂

    主要分析工业互联网产业联盟发布的工业互联网测试床之一--基于"5G+工业互联网"的无忧智能工厂. 测试床由鸿富锦精密电子(成都)有限公司+中国电信成都分公司共同研发.结合中国电信5 ...

最新文章

  1. VMware 12.0 在安装的时候出现 microsoft runtime dll
  2. 《易学Python》——1.8 总结
  3. Docker多容器连接-以Nginx+PHP为例
  4. mysql 6.3 入门_Mysql 入门小练习
  5. 简单多进程任务处理程序
  6. tf.truncated_normal
  7. vagrant 环境配置
  8. Python issubclass 函数 - Python零基础入门教程 2021-07-14 09:24:43
  9. 在webstorm中怎么配置本地服务器
  10. 算法:数组中的逆序对
  11. js layui跳转页面_layui自己添加图片按钮并点击跳转页面的例子
  12. 单片机练习 - 计时器
  13. Atitit 微服务实践 艾提拉著 微服务主要解决几个问题负载均很 目录 1. 微服务的模式 http请求层 vs服务层 1 1.1. Http vs 服务层优缺点 1 2. 实现技术 2
  14. java ship_用Java对象来解答世界悖论难题“忒修斯之船”
  15. Allegro中 板框 尺寸标注
  16. switchport mode access
  17. 我用这个画3d图和电路图、上网,防止鼠标手
  18. Ubuntu16.04 使用apt-get安装软件时无法自动安装所需要的依赖
  19. 第三周实验题目2——robots协议
  20. 把多个excel表数据合并到一个工作簿中怎么操作

热门文章

  1. 以写代学:python shutil模块
  2. 大白话讲解Promise(三)搞懂jquery中的Promise
  3. 单机多实例Tomcat部署
  4. 冲刺CCNA2.0(200-120)认证考试TK视频讲解
  5. 开源jeecms,jeebbs学习笔记4——从jo_user表看持久层设计
  6. oracle number +1,number number(1)
  7. linux硬盘类型怎么选,如何选择linux系统安装类型
  8. endnote大客户版_Endnote软件的使用,有图有干货!
  9. 文件路径存入mysql_网站的文件的上传,并将相对路径保存到数据库的代码实现。...
  10. QEventLoop进行函数运行进度控制