本周有一个新的开始,我已经实践TDD一周了,真正实践起来TDD,对于我这个老程序像是打开了另一扇编程的大门,收获良多,还需继续努力。
一、首先反省一下,为什么现在才开始实践TDD?
一方面因为之前对TDD的理解比较模糊,没有明确和有意识的去实施TDD,团队中的成员对TDD也是各有理解,甚至有反对的声音;另一方面最近遇到个挫折,也在启动Python端对,正好是一个非常棒的契机。
二、对TDD的新认知
有些人认为TDD是基于代码的单元测试,或者使用Mock等技术来驱动开发,抑或是帮助在重构、修改以后进行回归测试。反对的原因比如:
- 工期太紧,时间太短,TDD是在浪费时间;
- 业务需求变化太快,修改功能都来不及,根本没有精力来TDD;
- TDD对开发人员的素质要求非常高,普通的开发人员不行;
- 很多程序员还不会「写测试用例」和「重构」,TDD太遥远;
- 由于大量使用Mock等技术,对于测试业务价值作用不大
- ……
总的来说大家拒绝TDD的主要原因在于其难度大、工作量大、Mock的大量使用导致很难测试业务价值等。
过去的我也是真么认为的,但是经过一周的实践,我刷新了认知,也在调整我的研发习惯。
三、TDD的核心是什么?
再次强调,TDD的核心:先写测试,执行测试,遇错在写实现代码,由此帮助程序员来提升开发质量和效率。
先写测试,这里的测试并不只是单元测试,也不是说一定要使用mock、来做测试。这里的测试就是指软件测试本身,可以是基于代码单元的单元测试,可以是基于业务需求的功能测试,也可以是基于特定验收条件的验收测试。
TDD帮助开发人员,主要是帮助开发人员理解软件的功能需求和验收条件,帮助其思考和设计代码,从而达到驱动开发的目的,所以TDD是包含两部分内容:ATDD(验收测试驱动开发)与UTDD(功能测试驱动开发),http://www.agiledata.org/essays/tdd.html。
四、ATDD 和 UTDD
ATDD(Acceptance Test Driven Development):验收(功能)测试驱动开发,一般由BA或者QA编写验收测试用例,然后程序员通过验收测试来理解需求和验收条件,并编写实现代码直到验收测试用例通过。
由于验收方法和类型也是多种多样的,所以根据验收方法和类型的不同,ATDD其实是包含BDD(Behavior Driven Development)、EDD(Example Driven Development),FDD(Feature Driven Development)、CDCD(Consumer Driven Contract Development)等各种的实践方法。
比如以软件的行为为验收标准,这个是BDD;如果以特定的实例数据为验收标准,这个是EDD;如果以Web Service API消费者提出API契约来驱动API提供者开发API,这个是CDCD等。所以ATDD的具体实现需要结合项目的实际情况来选用适合的验收测试方法与类型。
UTDD(Unit Test Driven Development):单元测试驱动开发,首先Dev编写单元测试用例,然后编写实现代码直到单元测试通过。这个就是现在很多人所谓的TDD、实践的TDD、喜欢的TDD、抱怨的TDD,但是它却只是真正意义上TDD的一部分而已。
五、TDD金字塔
六、TDD流程图
七、开始你的TDD
书中的第一个例子:
from selenium import webdriver
browser = webdriver.Firefox()
browser.get("http://127.0.0.1:8000")
assert "Django" in browser.title
大致意思是使用Selenium编写的功能测试代码。打开Firefox浏览器,并访问http://127.0.0.1:8000,通过assert 判断浏览器标题中是否包含“Django”。
什么都不做的情况下运行的结果应为:
Traceback (most recent call last):
File "functional_tests.py", line 7, in <module>
assert "Django" in browser.title
AssertionError
测试失败了,这是必然的,因为我们还什么都没有做,这也叫预期失败,它同样也是我们想要的结果,因为失败了,所以我们要去做动作让测试通过。
八、我的第一个测试代码
在做一个授权店铺信息管理的功能,先从用户故事写起来,详见下面代码。
import unittest
from selenium import webdriver
class NewVistorTest(unittest.TestCase):
site_url = 'http://localhost:8000'
def setUp(self):
self.browser = webdriver.Firefox()
def tearDown(self):
self.browser.quit()
# 测试创建一个授权店铺
def test_can_create_a_shop(self):
# 小强打开 授权店铺应用地址
self.browser.get(self.site_url)
# 注意到页面标题包含"Shop-Auth"这个词
self.assertIn('Shop-Auth', self.browser.title)
# 访问授权店铺列表接口,在第一页没有发现"小强的小店"这个店铺信息
self.browser.get(self.site_url + '/shopAuthorization/shopList')
print(self.browser.page_source)
self.assertNotIn('小强的小店', self.browser.page_source)
self.fail('Finish the test!')
# 通过关键字"小强"搜索店铺信息,还是没有找到"小强的小店"这个店铺信息
# 看来真的没有,那么我们创建一个"小强的小店"
# 再次通过关键字"阿强"搜索店铺信息,找到了"小强的小店"
# 小强满意的露出笑容
if __name__ == '__main__':
unittest.main(warnings='ignore')
第一次练习,还有很多不足,Keep Leaning,Keep TDD。