【有书共读15】Python测试驱动开发 读书笔记07

编写这些测试有什么用

编程就像从井里打水
编程其实很难,我们的成功往往得益于自己的聪明才智。假如我们不那么聪明,TDD 就能 助我们一臂之力。
测试是一种技能,不是天生就会的。因为很多结果不会立刻显现,需要等待很长一段时 间,所以目前你要强迫自己这么做。

细化测试每个函数的好处 
就目前而言,测试简单的函数和常量看起来有点傻。你可能觉得不遵守这么严格的规 则,漏掉一些单元测试,应该也算得上是 TDD。但是在这本书中,我所演示的是完整 而严格的 TDD 流程。
像学习武术中的招式一样,在不受影响的可控环境中才能让技能 变成肌肉记忆。现在看起来之所以琐碎,是因为我们刚开始举的例子很简单。程序变 复杂后问题就来了,到时你就知道测试的重要性了。
你要面临的危险是,复杂性逐渐 靠近,而你可能没发觉,但不久之后你就会变成温水煮青蛙。 我赞成为简单的函数编写细化的简单测试,关于这一观点我还有这么两点要说。
 首先,既然测试那么简单,写起来就不会花很长时间。所以,别抱怨了,只管写就 是了。 其次,占位测试很重要。先为简单的函数写好测试,当函数变复杂后,这道心理障碍 就容易迈过去。
你可能会在函数中添加一个 if 语句,几周后再添加一个 for 循环,不 知不觉间就将其变成一个基于元类(meta-class)的多态树状结构解析器了。因为从一开始你就编写了测试,每次修改都会自然而然地添加新测试,最终得到的是一个测试 良好的函数。
相反,如果你试图判断函数什么时候才复杂到需要编写测试的话,那就 太主观了,而且情况会变得更糟,因为没有占位测试,此时开始编写测试需要投入很 多精力,每次改动代码都冒着风险,你开始拖延,很快青蛙就煮熟了。
 不要试图找一些不靠谱的主观规则,去判断什么时候应该编写测试,什么时候可以全 身而退。我建议你现在遵守我制定的训练方法,因为所有技能都一样,只有花时间学 会了规则才能打破规则。


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import unittest
class NewVisitorTest(unittest.TestCase):
 def setUp(self):
 self.browser = webdriver.Firefox()
 self.browser.implicitly_wait(3)
 def tearDown(self):
 self.browser.quit()
 def test_can_start_a_list_and_retrieve_it_later(self):
 # 伊迪丝听说有一个很酷的在线待办事项应用
 # 她去看了这个应用的首页
 self.browser.get('http://localhost:8000')
 # 她注意到网页的标题和头部都包含“To-Do”这个词
 self.assertIn('To-Do', self.browser.title)
 header_text = self.browser.find_element_by_tag_name('h1').text
 self.assertIn('To-Do', header_text)
 # 应用邀请她输入一个待办事项
 inputbox = self.browser.find_element_by_id('id_new_item')
 self.assertEqual(
 inputbox.get_attribute('placeholder'),
 'Enter a to-do item'
 )
 # 她在一个文本框中输入了“Buy peacock feathers(购买孔雀羽毛) ”
 # 伊迪丝的爱好是使用假蝇做鱼饵钓鱼
 inputbox.send_keys('Buy peacock feathers')
 # 她按回车键后,页面更新了
 # 待办事项表格中显示了“1: Buy peacock feathers”
 inputbox.send_keys(Keys.ENTER)
 table = self.browser.find_element_by_id('id_list_table')
 rows = table.find_elements_by_tag_name('tr')
 self.assertTrue(
 any(row.text == '1: Buy peacock feathers' for row in rows)
 )
 # 页面中又显示了一个文本框,可以输入其他的待办事项
 # 她输入了“Use peacock feathers to make a fly(使用孔雀羽毛做假蝇) ”
 # 伊迪丝做事很有条理
 self.fail('Finish the test!')
 # 页面再次更新,她的清单中显示了这两个待办事项
 [...]
我们使用了 Selenium 提供的几个用来查找网页内容的方法:find_element_by_tag_name,find_element_by_id 和 find_elements_by_tag_name(注意有个 s,也就是说这个方***返回 多个元素)。
还使用了 send_keys,这是 Selenium 在输入框中输入内容的方法。你还会看到使 用了 Keys 类(别忘了导入),它的作用是发送回车键等特殊的按键,还有 Ctrl 等修改键。 
小心 Selenium 中 find_element_by... 和 find_elements_by... 这两类函 数的区别。前者返回一个元素,如果找不到就抛出异常;后者返回一个列 表,这个列表可能为空。
还有,留意一下 any 函数,它是 Python 中的原生函数,却鲜为人知。如果你不懂 Python 的话,我告诉你,any 函数的参数是个生成器表达式(generator expression),类似于列表推导(list comprehension),但比它更为出色。
你需要仔细研究这 个概念。能搜到 Guido 对这一概念的精彩解释(http://python-history.blogspot.co.uk/2010/06/ from-list-comprehensions-to-generator.html)。
读完之后你就会知道,这个函数可不仅仅是为 了让编程惬意。 看一下测试进展如何:
 $ python3 functional_tests.py
 [...] 
selenium.common.exceptions.NoSuchElementException: Message: 'Unable to locate element: {"method":"tag name","selector":"h1"}' ; Stacktrace: [...] 
解释一下,测试报错在页面中找不到元素。看一下如何在首页的 HTML 中加入这个 元素。 大幅修改功能测试后往往有必要提交一次。初稿中我没这么做,想通之后就后悔了,可是 已经和其他代码混在一起提交了。
其实提交得越频繁越好:
 $ git diff # 会显示对functional_tests.py的改动 
$ git commit -am "Functional test now checks we can input a to-do item"

#笔记##读书笔记##测试##Python#
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务