• 9
  • 9
分享
  • Python应用实战—基于Selenium的功能测试——软件测试圈
  • 曼倩诙谐 2022-05-16 11:38:56 字数 11766 阅读 1605 收藏 9

  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笔者用下来不是非常好用,有的时候单元测试函数刷新不出来。

1-.png

  命令行单元测试执行效果:

1-2.png

  it's ok!

  1.2. selenium功能测试

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

  1.2.1. python环境安装Selenium

1-3.png

  键入selenuim,点击安装链接。

1-4.png

  1.2.2. 安装chromedriver驱动

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

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

1-5.png

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

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

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

  from  unittest import TestCase
  import django
  from selenium import webdriver
  class FunctionalTest(TestCase):
      @classmethod
      def setUpClass(cls): #①
          super(FunctionalTest, cls).setUpClass()
          django.setup()   
          cls.browser=webdriver.Chrome()
          cls.live_server_url = 'http://localhost:8001/task/'  
          
      @classmethod
      def 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-30b9142532fa
  F
  ======================================================================
  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 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 Task
  class FunctionalTest(LiveServerTestCase):
      @classmethod
      def setUpClass(cls): 
          super(FunctionalTest, cls).setUpClass()
          django.setup()   
          cls.browser=webdriver.Chrome()
          #cls.live_server_url = 'http://localhost:8001/task/'  
          #1初始化测试任务1
          data={'TaskNum':200,'Source':'101','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,}
          task = Task(**data)
          task.save()
          #2初始化测试任务2
          data={'TaskNum':201,'Source':'102','Target':'05-01-02','Barcode':'101001001009','State':1,'Priority':1,}
          task2 = Task(**data)
          task2.save()
          
      @classmethod
      def 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')
          #表第一行数据包含子任务数 10
          self.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_list
      self.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_id
      return 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 execute
      self.error_handler.check_response(response)
    File "C:\Python\Python36-32\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
      raise 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
      @classmethod
      def setUpClass(cls):
          super(TaskURLTest, cls).setUpClass()
          django.setup()
          #1初始化测试任务1
          data={'TaskNum':200,'Source':'101','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,}
          task = Task(**data)
          task.save()
          #2初始化测试任务2
          data={'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爽点之一。



作者:wuch   

来源:http://www.51testing.com/html/31/n-4477131.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 软件测试什么是软件软件=程序+数据库+文档+服务软件测试的定义软件测试是使用人工和自动手段来运行或测试某个系统的工程,其目的在于检验被测软件系统是否满足规定的需要,或是弄清楚被测系统的预期结果与实际结果之间的差别软件测试的误区如果有良好的设计和高水平的程序员,就不需要测试了软件测试并不创造任何代码和产品,可以不需要测试测试等与调试软件需求规格说明应详细的包含所有用户的需求软件测试可以提高软件质量测试没有技术含量软件缺陷的概念软件测试员认为软件难以理解软件未达到需求规格说明书中指明不会出现的错误软件出现了需求规格说明书中指明的范围软件功能超出需求规格说明书中指明的范围软件未达到需求规格说明书中虽...
            0 0 1677
            分享
          • 忙忙碌碌又一年,2021的计划完成了么?目标达成了么?买房、买车、脱单、加薪等等,都是很现实的事儿,2021了,80后步入40,90后步入30,曾经的追风少年,如今的油腻大叔,突然感觉时间好残酷,职场也一样,竞争、裁员,不知道下一刻的命运,只有强者才能主宰自己的命运,无畏未来,正因为如此,大城市才兴起了很多付费的自习室,静心提升。作为一名测试人员,如何成为强者呢?如果想要在测试领域好好发展,提升自己的测试技术是必不可少的,但是,选对方向更为重要,功能测试、自动化测试、测试开发、性能测试、安全测试、测试管理,每个测试方向都不简单,但是,大环境就是,你不会点代码、熟悉两个框架原理啥的,都是算low...
            0 0 853
            分享
          •   今天给大家分享下,大田在平常工作中,使用 Jmeter 做完接口测试后,如何去简单做个压力测试,看本次测试接口的承载能力。  在接口测试的基础上增加一个元件:聚合报告。  步骤  1、准备好批量的压测数据,这个数据是来源于接口测试过程,将数据保存在 CSV 或者 txt 文件中;  2、创建 CSV 元件,配置好文件路径,以及各个变量名称,应为数据文件中各个表头数据;  3、在 HTTP 请求中使用 ${} 去引用上述图中各个变量,如:${变量名1};  4、在线程组中设置要并发的数量即下图中的线程数,还可以勾选调度器,设置持续时间和启动线程组时延迟时间。  备注  大...
            0 0 1582
            分享
          • webdriver概述是一个非常好用的用来进行复杂重复的web自动化测试的工具,主要是,它可以用于我们进行爬虫。WebDriver(Selenium 2),它的主要新功能是集成了 Selenium 1.0 以及 WebDriver。WebDriver(Selenium 2) 兼容 Selenium,它既支持 Selenium API 也支持 WebDriver API。安装chrome webdriver(这是谷歌浏览器的webdriver安装),安装webdriver必须要和对应浏览器的版本相匹配才可以使用。安装之前,先查看浏览器的版本,我这里用谷歌浏览器。1. 查看自己的chrome谷歌浏...
            12 12 3078
            分享
          •        谈到浏览器兼容性测试,很多同学往往考虑到的都是如何覆盖全面,管它什么Chrome、FireFox、IE、Safari这种主流浏览器还是360、QQ、搜狗等一系列国产浏览器,都给它列到列表上边去,只要时间允许,就一个一个测起来。但是大家有没有想过,这个策略真的是正确的么?真的应该这样去进行测试么?       在回答上面的问题之前,我们先来思考这样一个问题:我们为什么要做兼容性测试? 或者换个说法,网站为什么会在不同的浏览器上展现出不同的样式?毫无疑问,其答案是因为内核不同、渲染方式不同。有了这样的结果...
            5 8 5760
            分享
      • 51testing软件测试圈微信