Open Workshop是由ThoughtWorks举办的活动,这次活动的主题是 =TDD测试驱动开发和安全攻防,活动时间是6月21日周六,我参加了java的TDD,虽然最近一直在搞js和scala,但java还是不会拉下的。
至于为什么周六的事情,周一来写,则是由于参加完活动后,便一直忙于用scaloid
写android
UI组件,直到今天上班,在路途中一直在思考如何给完全没有测试代码的公司代码引入测试机制时,把周六的课程细细的整理了下。
TDD与其说是一种技术,还不如说是一种思想,一种在明确需求下,拆分需求,理顺实现思路的思想。当然,需求一定要明确,如果需求都不明确的话,写越多的测试,意味着做越多的无用功。我想,这也可能是为什么TDD在大公司里能更好发展的一重要原因吧(当你经历过一周内大改三次需求时...)
而对于我个人来讲,为什么会觉得TDD比较好,是因为由于性格原因,需求一来,代码就敲起来。这造成由于对需求解析的不够,代码很容易作废。代码作废2次对我来讲,很正常(囧)。经过TDD对需求的梳理,这种情况应该能大为缓解。
整个课程由禚娴静老师教授,由10人以结对编程的方式参与。参与的人背景从大四实习到八年架构师,老实说,把我震惊了一把。
看着风格迥异的代码,你会发现,原来代码还可在这么写!
下面则是我在TDD课程中的收获
@Before
的意思就是,在运行每一个测试单元之前,都需要运行规定在@Before
内的代码块。(在一些框架内也作为BeforeEach
)
在这个地方,我遇到了整个课程中最大的思想颠覆点。
在写测试的时候,我将代码的简洁性放在首位,也就是说,会把数据准备和类准备都放在这里。
describe('tempHelper ', function() { var t1, t2, div1, div2, data1, data2, fn1, container, fn2, div3, fn3, d1, d2, d3; var t4, div4, d4, fn4, data3; beforeEach(function() { div1 = setFixtures("<div id='div1'></div>") div2 = setFixtures("<div id='div2'></div>") div3 = setFixtures("<div id='div3'></div>") div4 = setFixtures("<div id='div4'></div>") $.ajax = mocka.mockView() jasmine.clock().install() data1 = { str: "value", num: 3, arr: ["v1", "v2"] } data2 = { str1: "value", num1: 3, arr1: ["v1", "v2"] } data3 = { elems: [1, 2, 3] } fn1 = function() {} fn2 = function() {} fn3 = function() {} fn4 = function() {} container = { fn1: fn1, fn2: fn2, fn3: fn3, fn4: fn4 } spyOn(container, "fn1") spyOn(container, "fn2") spyOn(container, "fn3") spyOn(container, "fn4") render(data1, "tempHelperSpec_1", div1, function() { container.fn1() t1 = jc.th(div1) d1 = t1.data() }) render(jc.mix(true, {}, data1), "tempHelperSpec_1", div2, function() { container.fn2() t2 = jc.th(div2) d2 = t2.data() }) render(data2, "tempHelperSpec_1", div3, function() { container.fn3() t3 = jc.th(div3) d3 = t3.data() }) jc.render(jc.mix(true, {}, data1), "tempHelperSpec_2", div4, function() { container.fn4() t4 = jc.th(div4) d4 = t4.data() }) }) afterEach(function() { jasmine.clock().uninstall(); session = {}; //清空掉session }) describe("render", function() { it("can render the same div again", function() { jc.render(jc.mix(true, {}, data1), "tempHelperSpec_2", div4, function() { container.fn4() t4 = jc.th(div4) d4 = t4.data() }) }) }) //....
而在TDD中,则应该将数据放在单元测试中,这样做的好处
便于阅读,不用上下翻看
少初始化东西,加快运行速度
以上两点,我认同,但在我现在的工作环境中,测试代码就我一个人看,一个人写,一个人维护。而速度,对有自动化工具(grunt watch
)的js代码来讲,影响微乎其微。
首先来说下测试case的准备分类吧
dummy
mock
fake
...//没记住...
我搭建的第一个真正意义上的测试环境是在scala akka上的,然后每个一个测试的执行需要两个以上的mock actor注入,也就是mock会充斥整个项目单元测试的执行(在写完几个测试后,由于一些原因离职,未能继续跟进)。当时我以为这是正确的做法,甚至打算在现在公司的项目中也填入mock,对ajax请求 数据库等进行mock。在参加完这个活动以及查阅了相关资料后(http://www.infoq.com/cn/articles/thoughtworks-practice-partvi ),要慎重重新评估了。
当你需要通过写一些函数方法,来帮助测试时,如果这些函数方法也能为开发提供便利时,应当将它提取到底层公共函数库中去。
虽然第一次接触TDD,是在大四学习Ruby on Rails网站开发,但也就仅仅是知道了个spec语法(边学代码,边写测试,对于代码初学者来讲,太影响积极性了),后来也就扔掉了。现在重新学习了一遍,才知道以前根本就不懂什么是TDD。最后,感谢ThoughtWorks提供这么一个舞台,让我们能够结识不同岗位上的同行,也感谢禚娴静老师的精彩演讲。当然了,还有我的pair。
PS:据说ThoughtWorks在上海举办了scala的活动,北京不能被遗忘啊!