2017-04-21 08:53:41 huver2007 阅读数 743
  • SCRUM敏捷开发视频教程

    SCRUM敏捷开发视频教程,该课程为你分享SCRUM敏捷开发,理解敏捷的本质,认识中国IT行业对敏捷的挑战,学会让敏捷落地的实用招数。 嘉宾介绍:张传波 1. 创新工场创业课程(敏捷课程)讲师 2.软件研发管理佳实践顾问(曾任华为某团队研发顾问) 3. 中国敏捷联盟《ADBOK》(敏捷开发知识体系)项目组成员 二十年软件开发、软件设计、需求分析、项目管理、部门管理、公司管理及过程改进等经验,亲历“无数”项目,涉猎建筑、通讯、互联网、电力、金融、制造业、政府等领域,熟悉软件生命周期的全部过程

    10404 人正在学习 去看看 CSDN讲师

测试驱动开发

测试驱动开发基本思想是,开发人员先编写测试用例,再编写能通过用例的代码。就像砌砖师傅先用桩子拉上线再砌墙一样。

思想:

•       Test Driven Development测试驱动开发

•       Acceptance TestDriven Development验收测试驱动开发

•       测试驱动设计,测试也是一项设计活动,编写用例的同时已在思考设计

•       持续集成,用例必须能自动化,且集成到CI环境中

优点:

•       提高代码行测试覆盖率

•       100%通过率,处理不通过的问题是优先级最高任务

•       快速变更的能力,变更后快速通过持续集成自动化测试

•       有信心重构,重构后的代码能快速测试

•       减少技术债,减少BUG泄露

敏捷转型过程中,测试驱动开发通常是最重要也最艰难的一个,只有实现快速自动化测试,才可能快速交付。前景非常诱人,但是在这个过程中我们的付出可能也是最多的。

按照敏捷思想,大部分的测试工作应该是开发来完成的,测试人员重点是保证开发测试的正确性,比如:测试方案、测试计划等。测试驱动开发的推广过程中,首要的问题是将开发人员长期以来形成的思维观念和意识形态转变过来,大部分开发人员只喜欢编码,不喜欢测试,更无法理解为什么没有产品代码的时候就先写单元测试;其次是相关的技术支持,测试驱动开发对开发人员提出了更高的要求,不仅要掌握测试和重构,还要懂得设计模式等设计方面的知识。

那么测试驱动开发跟持续集成如何关联到一起哪?简单总结一下:

•     Add atest

•     Addcode to pass the test

•     Passall existing tests

•     Cleanup the code

•     Checkin

•     ContinuousIntegration                        =

(   Auto Compile                                      +

Auto Build                                            +

Auto Deploy                                         +

Auto fullregression testing                 +    

Check in codefrequently                      )

 

 

2018-07-27 10:27:31 weixin_41845533 阅读数 3188
  • SCRUM敏捷开发视频教程

    SCRUM敏捷开发视频教程,该课程为你分享SCRUM敏捷开发,理解敏捷的本质,认识中国IT行业对敏捷的挑战,学会让敏捷落地的实用招数。 嘉宾介绍:张传波 1. 创新工场创业课程(敏捷课程)讲师 2.软件研发管理佳实践顾问(曾任华为某团队研发顾问) 3. 中国敏捷联盟《ADBOK》(敏捷开发知识体系)项目组成员 二十年软件开发、软件设计、需求分析、项目管理、部门管理、公司管理及过程改进等经验,亲历“无数”项目,涉猎建筑、通讯、互联网、电力、金融、制造业、政府等领域,熟悉软件生命周期的全部过程

    10404 人正在学习 去看看 CSDN讲师

Python测试驱动开发(TDD)

前言:TDD是一种敏捷开发模式,而不是测试方法
测试很难 ——- 难在坚持,一直做下去。
现在花时间编写的测试不会立即显出功效,要等到很久以后才有作用 ——- 或许几个月之后避免在重构过程中引入问题,或者升级依赖时捕获回归异常。或许测试会从一种很难衡量的方式回报你,促使你写出设计更好的代码,但你却误以为没有测试也能写出如此优雅的代码。

项目github地址

https://github.com/Tyrone-Zhao/Test-Driven-Development

目录

单元测试的好处

编程就像从井里打水

编程其实很难,我们的成功往往得益于自己的聪明才智。假如我们不那么聪明,TDD就能助我们一臂之力。Kent Beck(TDD理念基本就是他发明的)打了个比方。试想你用绳子从井里提一桶水,如果井不太深,而且桶不是很满,提起来很容易。就算提满满一桶水,刚开始也很容易。但要不了多久你就累了。TDD理念好比是一个棘轮,你可以使用它保存当前的进度,休息一会儿,而且能保证进度绝不倒退。这样你就没必要一直那么聪明了。Test All The Things!

细化测试每个函数的好处

程序变复杂后问题就来了,到时你就知道测试的重要性了。你要面临的危险是,复杂性逐渐靠近,而你可能没发觉,但不久之后你就会变成温水煮青蛙。
首先,写测试很简单,写起来不会花很长时间,所以,别抱怨,只管写就是了。
其次,占位测试很重要。先为简单的函数写好测试,当函数变复杂后,这道心理障碍就容易迈过去。你可能会在函数中添加一个if语句,几周后再添加一个for循环,不知不觉间就将其变成一个基于元类(meta-class)的多态树结构解析器了。因为从一开始你就编写了测试,每次修改都会自然而然地添加新测试,最终得到的是一个测试良好的函数。相反,如果你试图判断函数什么时候才复杂到需要编写测试的话,那就太主观了,而且情况会变得更糟,因为没有占位测试,此时开始编写测试需要投入很多精力,每次改动代码都冒着风险,你开始拖延,很快青蛙就煮熟了。

单元测试与功能测试的区别

单元测试和功能测试之间的界线有时不那么清晰。不过二者之间有个基本区别:功能测试站在用户角度从外部测试应用,单元测试则站在程序员的角度从内部测试应用。
采用的工作流程大致如下:
1)先写功能测试,从用户的角度描述应用的新功能
2)功能测试失败后,想办法编写代码让它通过(或者说至少让当前失败的测试通过)。此时,使用一个或多个单元测试定义希望代码实现的效果,保证为应用中的每一行代码(至少)编写一个单元测试
3)单元测试失败后,编写最少量的应用代码,刚好让单元测试通过。有时,要在第2步和第3步之间多次往复,直到我们觉得功能测试有一点进展为止。
4)然后,再次运行功能测试,看能否通过,或者有没有进展。这一步可能促使我们编写一些新的单元测试和代码等。

由此可以看出,在整个过程中,功能测试站在高层驱动开发,而单元测试从低层驱动我们做些什么。


功能测试代码, 测试主要功能(冒烟测试),数据有效性验证。详细代码可参见上面的github地址中的functional_test/

TDD的重要思想是必要时一次只做一件事。即每次只做必要的操作,让功能测试向前迈出一小步即可。


单元测试代码,视图逻辑测试,数据模型测试,模版表单测试。
Mock,参数校验:

from unittest.mock import patch, call
[...]
    @patch("accounts.views.auth")
    def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
        self.client.get("/accounts/login?token=abcd123")
        self.assertEqual(
            mock_auth.authenticate.call_args,
            call(uid="abcd123")
        )
  • 这里的patch mock了accounts.views.auth模块,而且把mock的范围限定在下面的测试函数范围内,测试函数执行完毕,mock即被取消
  • mock掉的auth模块中的所有属性和方法也同样是mock对象,通过在参数重传递mock对象为mock_auth来实现mock对象的调用
  • self.client.get()方法为django内置的模拟客户端,可以模拟客户端发送请求,示例中请求了”/accounts/login?token=abcd123”的url
  • 对应请求url的回调函数为auth模块中的authenticate方法,用mock对象的call_args属性获取其请求的参数,返回一个call(*args, **args)对象
  • 下面用mock模块中导入的call方法构建预期结果,使用TestCase类的assertEqual()方法断言结果,至此一个单元测试函数完毕

单元测试与集成测试的区别以及数据库

追求纯粹的人会告诉你,真正的单元测试绝不能设计数据库操作。不仅测试代码,而且还依赖于外部系统,如数据库的测试叫做集成测试更确切。


“单元测试/编写代码“循环

TDD中的单元测试/编写代码循环
1)在终端里运行单元测试,看它们是如何失败的。
2)在编辑器中改动最少量的代码,让当前失败的测试通过
然后不断重复。
想保证编写的代码无误,每次改动的幅度就要尽量小。这么做才能确保每一部分代码都有对应的测试监护。
乍一看工作量很大,初期也的确如此。但熟练之后你便会发现,即使步伐迈得很小,编程的速度也很快。

良好的单元测试实践方法要求,一个测试只能测试一个功能,多个功能需要写成多个测试。因为如果一个测试中有多个断言,一旦前面的断言导致测试失败,就无法得知后面的断言情况如何。

遵守不测试常量规则

单元测试要测试的其实是逻辑、流程控制和配置。编写断言检测HTML字符串中是否有指定的字符序列,不是单元测试应该做的。

重构的首要原则是不能没有测试,严格的TDD流程中,可以遵循以下顺序:
功能测试 -> 单元测试 -> 单元测试/编写代码循环 -> 重构代码

有用的TDD概念

  • 回归
    新添加的代码破坏了应用原本可以正常使用的功能。
  • 意外失败
    测试在意料之外失败了。这意味着测试中有错误,或者测试帮我们发现了一个回归,因此要在代码中修正。
  • 遇红/变绿/重构
    描述TDD流程的另一种方式。先编写一个测试看着它失败(遇红),然后编写代码让测试通过(变绿),最后重构,改进实现方式。
  • 三角法
    添加一个测试,专门为某些现有的代码编写用例,以此推断出普适的实现方式(在此之前的实现方式可能作弊了)。
  • 事不过三,三则重构
    判断何时删除重复代码使用的经验法则。如果两段代码很相似,往往还要等到第三段相似代码出现,才能确定重构时哪一部分是真正共通、可重用的。
  • 记在便签上的待办事项清单
    在便签上记录编写代码过程中遇到的问题,等手头的工作完成后再回过头来解决。

三种功能测试调试技术:行间print语句、time.sleep以及改进的错误消息:
如assert[Equal|In|True|其他](something, “错误消息”)

不要预先做大量的设计

TDD和软件开发中的敏捷运动联系紧密。敏捷运动反对传统软件工程实践中预先做大量设计的做法。敏捷理念认为,在实践中解决问题比理论分析能学到更多,要尽早把最简可用应用放出来,根据实际使用中得到的反馈逐步向前推进设计。当然,稍微思考一下设计往往能帮我们更快地找到答案。

使用递增的步进式方法修改现有代码,而且保证代码在修改前后都能正常运行。

YAGNI
关于设计的思考一旦开始就很难停下来,我们会冒出各种想法:或许想给每个清单起个名字或加个标题,或许想使用用户名和密码识别用户,或许想给产品页面添加一个较长的备注和简短的描述,或许想存储某种顺序,等等。但是,要遵守敏捷理念的另一个信条:”YAGNI”(读作yag-knee)。它是”You ain’t gonna need it”(你不需要这个)的简称。
有时我们冒出一个想法,觉得可能需要,可问题是,不管想法有多好,大多数情况下最终你都用不到这个功能。

REST(式)
“表现层状态转化”(representational state transfer, REST)是Web设计的一种方式。设计面向用户的网站时,不必严格遵守REST规则,可是从中能得到一些启发。想看看REST API是什么样子,可以查看我的另一篇博文。

确保出现回归测试

独立用户场景下的功能测试通过后,要注意多用户场景下的功能测试回归。
重构代码,或者开发新功能时修改了旧有代码,要注意单元测试和功能测试的回归。

如何测试设计和布局

简单来说,不应该为设计和布局编写测试。因为这太像是测试常量,所以写出的测试不太牢靠
这说明设计和布局的实现过程极具技巧性、涉及CSS和静态文件。因此,可以编写一些简单的“冒烟测试“,确认静态文件和CSS起作用即可。把代码部署到生产环境时,冒烟测试能协助我们发现问题。
但是如果某部分样式需要很多客户端JavaScript代码才能使用(如动态缩放),就必须为此编写一些测试。
要试着编写最简的测试,确信设计和布局起作用即可,不必测试具体的实现。我们的目标是能自由修改设计和布局,且无须时不时地调整测试。

功能测试代码, 布局和样式测试:

from selenium.webdriver.common.keys import Keys
from .base import FunctionalTest


class LayoutAndStylingTest(FunctionalTest):

    def test_layout_and_styling(self):
        # 小明访问首页
        self.browser.get(self.live_server_url)
        self.browser.set_window_size(1024, 768)

        # 他看到输入框完美地居中显示
        inputbox = self.get_item_input_box()
        self.assertAlmostEqual(
            inputbox.location["x"] + inputbox.size["width"] / 2,
            512,
            delta=10
        )

        # 他新建了一个清单,看到输入框仍完美地居中显示
        inputbox.send_keys("测试")
        inputbox.send_keys(Keys.ENTER)
        self.wait_for_row_in_list_table("1: 测试")
        inputbox = self.get_item_input_box()
        self.assertAlmostEqual(
            inputbox.location["x"] + inputbox.size["width"] / 2,
            512,
            delta=10
        )

TDD以及部署的危险区域

部署的过程中一些危险区域如下:
1)静态文件(CSS、JavaScript、图片等)
Web服务器往往需要特殊的配置才能伺服静态文件
2)数据库
可能会遇到权限和路径问题,还要小心处理,在多次部署之间不能丢失数据
3)依赖
要保证服务器上安装了网站依赖的包,而且版本要正确

不过这些问题有相应的解决方案:
1)使用与生产环境一样的基础架构部署过渡网站(staging site),这么做可以测试部署的过程,确保部署真正的网站时操作正确。
2)可以在过渡网站中运行功能测试,确保服务器中安装了正确的代码和依赖包。而且为了测试网站的布局,我们编写了冒烟测试,这样就能知道是否正确加载了CSS。
3)与在本地设备上一样,当服务器上运行多个Python应用时,可以使用虚拟环境管理包和依赖。
4)最后,一切操作都自动化完成。使用自动化脚本部署新版本,使用同一个脚本把网站部署到过渡环境和生产环境,这么做能尽量保证过渡网站和线上网站一样。

调试技巧
查看Nginx的错误日志,存储在/var/log/nginx/error.log中。
检查Nginx的配置:nginx -t
确保浏览器没有缓存过期的响应。按下Ctrl键的同时点击刷新按钮。

别忘了重构

TDD不是万能灵药。它要求你在测试通过后花点实践重构,改进设计。否则“技术债务“将高高筑起。
不过,重构的最佳方法往往不那么容易想到,可能等到写下代码之后的几天、几周甚至几个月,处理完全无关的事情时,突然灵光一闪才能想出来。

在解决其他问题的途中,应该停下来去重构以前的代码吗?
要视情况而定。不能冒险在无法正常运行的应用中重构,可以在便签上做个记录,等测试组件能全部通过之后再重构。

关于重构的小贴士
1)把测试放在单独的文件夹中
功能测试可以按照特定功能或用户故事的方式组织。
单元测试分拆成文件,放在一个Python包中。
2)编写测试的主要目的是让你重构代码!一定要重构,尽量让代码(包括测试)变得简洁。
3)测试失败时别重构
如果测试的对象还没实现,可以先为测试方法加上@skip装饰器。
记下想重构的地方,完成手头上的活儿,等应用处于正常状态时再重构。
提交代码之前别忘了删掉所有@skip装饰器!

尽早合并代码

精益理论中的“尽早部署“有个推论,即“尽早合并代码“。编写表单可能要花很多时间,不断添加各种功能—做了各种工作,得到一个功能完善的表单类,但发布应用后才发现大多数功能实际并不需要。
因此,要尽早试用新编写的代码。

要判断何时应该编写测试确认我们没有犯错

测试时要判断何时应该编写测试确认我们没有犯错。一般而言,做决定时要谨慎。
不可能编写测试检查所有可能出错的方式。如果有一个函数计算两数之和,可以编写一些测试:

    assert adder(1, 1) == 2
    assert adder(2, 1) == 3

但不应该认为实现这个函数时故意编写了有违常理的代码:

    def adder(a, b):
        # 不可能这么写
        if a == 3:
            return 666
        else:
            return a + b

判断时你要相信自己不会故意犯错,只会不小心犯错。

单元测试需要一个断言框架和报告程序,或许还要选择一个技术模拟库

在JavaScript领域,测试工具的选择有许多种,如jsUnit、Qunit、Mocha、Chutzpah、Karma、Testacular、Jasmine等。选择其中一个工具后,还得选择一个断言框架报告程序,或许还要选择一个驭件(spy侦件、fake伪件、stub桩件)技术库。

示例项目中使用的是QUnit,简单,根Python单元测试很像,而且能很好地和jQuery配合使用。
代码可以参考上面的github地址中list/static/tests/test.html
Qunit

JavaScript测试在TDD循环中的位置

JavaScript测试在双重TDD循环中处于什么位置?答案是,JavaScript测试和Python单元测试扮演的角色完全相同。
1)编写一个功能测试,看着它失败
2)判断接下来需要哪种代码,Python还是JavaScript?
3)使用选中的语言编写单元测试,看着它失败。
4)使用选中的语言编写一些代码,让测试通过。
5)重复上述步骤

一些缺憾

1)编写JavaScript时,应该尽量利用编辑器提供的协助,避免常见的问题。试一下句法/错误检查工具,如jsLinter、jshint。
2)使用Phantomjs可以让JavaScript测试在命令行中运行。
Phantomjs
3)前端开发圈目前流行angular.js和React这样的MVC框架。这些框架的教程大都使用一个RSpec式断言库,名为Jasmine。如果你想使用MVC框架,使用Jasmine比Qunit更方便。

探索性编程、探究及去掉探究代码

学习新工具,或者研究新的可行性方案时,一般都可以适当地把严格的TDD流程放在一边,不编写测试或编写少量的测试,先把基本的原型开发出来。
这种创建原型的过程一般叫作“探究“(spike)。

最好在一个新分支中去探究,去掉探究代码时再回到主分支。

把探究所得应用到真实的代码基中。要完全摒弃探究代码,然后从头开始,用TDD流程再实现一次。去掉探究代码后实际编写的代码往往与最初有很大不同,而且通常更好。
该不该这么做请视情况而定!

在Python中使用模拟技术

模拟技术,是在单元测试中测试外部依赖的通用方式。只要与第三方API交互都适合使用驭件测试。
代码有外部副作用时也是如此,例如调用API、发推文、发短信等等。我们并不想真的通过互联网发推文或者调用API。但又必须找到一种方法,测试代码是否正确。驭件(mock)正是我们寻找的答案。

from unittest.mock import patch


@patch("accounts.views.auth")
class LoginViewTest(TestCase):
    ''' 登录视图测试 '''

    [...]

    @patch("accounts.views.send_mail")
    def test_sends_link_to_login_using_token_uid(self, mock_send_mail,
                                                 mock_auth):
        ''' 测试含有token的登录链接被发送到指定邮件地址 '''
        self.client.post("/accounts/send_login_email", data={
            "email": "200612453@qq.com"
        })

        token = Token.objects.first()
        expected_url = f"http://testserver/accounts/login?token={token.uid}"
        (subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
        self.assertIn(expected_url, body)

使用驭件可能导致“与实现紧密耦合“。我们知道,通常最好测试行为,而不测试实现细节;测试发生了什么,而不测试是如何发生的。驭件往往在如何做这条路上走的太远,而很少关注“是什么“。
如果能有效减少测试之间的重复,就有充分的理由使用驭件。这是避免组合爆炸的一种方式。

把ORM代码放到辅助方法中

有些人喜欢尽量减少应用中使用的ORM代码量,尤其不喜欢在视图层和表单层使用ORM代码。
一个原因是,测试这几层时更容易;另一个原因是,必须定义辅助方法,这样能更清晰地表述域逻辑。请对比这段代码:

list_ = List()
list_.save()
item = Item()
item.list = list_
item.text = self.cleaned_data["text"]
item.save()

和这段代码:

List.create_new(first_item_text=self.cleaned_data["text"])

辅助方法同样可用于读写查询。假设有这样一行代码:

Book.objects.filter(in_print=True, pub_date__lte=datetime.today())

和如下的辅助方法相比,孰好孰坏一目了然:

Book.all_available_books()

定义辅助方法时,可以起个适当的名字,表明它们在业务逻辑中的作用。使用辅助方法不仅可以让代码的条理变得更清晰,还能把所有ORM调用都放在模型层,因此整个应用不同部分之间的耦合更松散

以复杂度为准则

处理复杂问题时才能体现隔离测试(真正的单元测试)的优势。
一旦应用变得复杂,比如视图和模型之间分了更多层,需要编写辅助方法或自己的类,那多编写一些隔离测试就能从中受益了。

不同测试类型以及解耦ORM代码的利弊

1)功能测试
从用户的角度出发,最大程度上保证应用可以正常运行。
但反馈循环用时长。
无法帮助我们写出简介的代码。
2)整合测试(依赖于ORM或框架(如Django、Flask)测试客户端等)
编写速度快。
易于理解。
发现任何集成问题都会提醒你。
但是,并不总能得到好的设计(这取决于你自己)。
一般运行速度比隔离测试慢。
3)隔离测试(使用驭件)
涉及的工作量最大。
可能难以阅读和理解。
但是,这种测试最能引导你实现更好的设计。
运行速度最快。
4)解耦应用代码和ORM代码
力求隔离测试的后果之一是,我们不得不从视图和表单等处删除ORM代码,把它们放到辅助函数或者辅助方法中。如果从解耦应用代码和ORM代码的角度看,这么做有好处,还能提高代码的可读性。
当然,要结合实际情况判断是否值得付出额外精力去做。

CI和Selenium最佳实践

把Jenkins安装在过渡服务器或生产服务器上可不是个好主意,因为有很多操作要交给Jenkins完成,比如重新引导过渡服务器。
为了提升Jenkins的安全性,还要设置HTTPS。可以让Nginx使用自签名的证书,把发给443端口的请求转发给8080端口。这样设置之后,甚至可以让防火墙阻断8080端口。

设置虚拟显示器,在无界面的环境中运行功能测试

1)无界面浏览器(headless browser),如PhantomJS或SlimerJS。
2)设置虚拟显示器,使用”Xvfb”,MAC需要自己下载XQuartz,Jenkins插件中配置/opt/X11/bin/
3)Jenkins中启用虚拟显示器勾选”Start Xvfb before the build, and shut it down after.”
4)自己在功能测试脚本中添加失败截图方法,方便调试(因为没有没显示器)。
5)用Fabric或Ansible等工具将通过单元测试的构建自动部署到过渡服务器并进行功能测试。
6)部署到生产服务器
Jenkins

在不同的测试类型之间正确权衡

1)隔离测试(纯粹的单元测试)与整合测试
单元测试的主要作用应该是验证应用的逻辑是否正确。隔离测试只能测试一部分代码,测试是否通过与其他任何外部代码都没有关系。纯粹的单元测试是指,对于一个函数的测试而言,只有这个函数能让测试失败。如果这个函数依赖于其他系统且破坏这个系统会导致测试失败,就说明这是整合测试。这个系统可以是外部系统,例如数据库,也可以是我们无法控制的另一个函数。不管怎样,只要破坏系统会导致测试失败,这个测试就没有完全隔离,因此也就不是纯粹的单元测试。整合测试并非不好,只不过可能意味着同时测试多个功能。
2)集成测试
集成测试用于检查被你控制的代码是否能和你无法控制的外部系统完好集成。集成测试往往也是整合测试。
3)系统测试
如果说集成测试检查的是与外部系统的集成情况,那么系统测试就是检查应用内部多个系统之间的集成情况。例如,检查数据库、静态文件和服务器配置在一起是否能正常运行。
4)功能测试和验收测试
验收测试的作用是从用户的角度检查系统是否能正常运行。(用户能接受这种行为吗?)验收测试很难不写成全栈端到端测试。前文中,使用端到端测功能测试代替验收测试和系统测试。

要熟练的编写单元测试,需要进入”神赐的心流状态(形容极度专注)”。XD。
如果应用的核心使用函数式编程范式编写(完全没有副作用),因此可以使用完全隔离、纯粹的单元测试,根本无需使用驭件
1> 务实为本
跟着感觉走,先编写下意识觉得应该编写的测试,然后再根据需要修改。在实践中学习。
2> 关注想从测试中得到什么
目标是正确性、好的设计和快速的反馈循环
3> 架构很重要
架构在某种程度上决定了所需的测试类型。业务逻辑与外部依赖隔离得越好,代码的模块化成都越高,在单元测试、集成测试和端到端测试之间便能达到越好的平衡。

使用Sinon.js测试客户端Ajax

没有模拟库是无法测试Ajax的。不同的测试框架和工具采用不同的模拟库,而Sinon是通用的。Sinon还提供了JavaScript驭件。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Javascript tests</title>
    <link rel="stylesheet" type="text/css" href="qunit-2.6.1.css">
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture">
        <form id="id_item_form">
            <input name="text" />
            <input type="hidden" name="csrfmiddlewaretoken" value="tokey" />
            <div class="has-error">
                <span class="help-block">Error Text</span>
            </div>
        </form>
        <table id="id_list_table">
        </table>
    </div>

    <script src="../node_modules/jquery/dist/jquery.min.js"></script>
    <script src="../node_modules/sinon/pkg/sinon.js"></script>
    <script src="qunit-2.6.1.js"></script>
    <script src="list.js"></script>

    <script type="text/javascript">
        /* global sinon */

        var server, sandbox;
        QUnit.testStart(function(){
            server = sinon.fakeServer.create();
            sandbox = sinon.createSandbox();
        });
        QUnit.testDone(function(){
            server.restore();
            sandbox.restore();
        });

        QUnit.test("should call updateItems on initialize", function (assert) {
            var url = "/getitems/";
            sandbox.spy(window.Superlists, "updateItems");
            window.Superlists.initialize(url);
            assert.equal(
                window.Superlists.updateItems.lastCall.args,
                url
            )
        });

        [...]

可以通过server.respond()准确控制异步代码的流程:

 QUnit.test("updateItems should fill in lists table from ajax response", function (assert) {
            var url = "/getitems/";
            var responseData = [
                {"id": 101, "text": "待办事项1"},
                {"id": 102, "text": "待办事项2"},
            ];
            server.respondWith("GET", url, [
                200, {"Content-Type": "application/json"}, JSON.stringify(responseData)
            ]);
            window.Superlists.updateItems(url);

            server.respond();

            var rows = $("#id_list_table tr");
            assert.equal(rows.length, 2);
            var row1 = $("#id_list_table tr:first-child td");
            assert.equal(row1.text(), "1: 待办事项1")
            var row2 = $("#id_list_table tr:last-child td");
            assert.equal(row2.text(), "2: 待办事项2");
        });

1)需要注意,初始化之后才能设置服务器相应,因为我们想设置的是提交表单时发送的POST请求的响应,而不是一开始那个GET请求的响应。JavaScript测试最难掌握的技术之一便是控制执行顺序。
2)仅当开始那个GET请求处理完毕之后,才开始模拟辅助函数。sandbox.spy调用的作用与Python测试中的patch一样,把指定对象替换为驭件。
3)模拟的updateItems函数现在多了一些属性,例如lastCall和lastCall.args(类似于Python驭件的call_args)。

TDD速查表

——————未完待续———————-

项目开始阶段

TDD基本流程

测试不止要在开发环境中运行

通用的测试最佳实践

Selenium功能测试最佳实践

由外而内,测试隔离与整合测试,模拟技术

参考资料: 《Python测试驱动开发》第2版、《Python测试之道》

2017-07-18 22:58:30 lanchunhui 阅读数 2792
  • SCRUM敏捷开发视频教程

    SCRUM敏捷开发视频教程,该课程为你分享SCRUM敏捷开发,理解敏捷的本质,认识中国IT行业对敏捷的挑战,学会让敏捷落地的实用招数。 嘉宾介绍:张传波 1. 创新工场创业课程(敏捷课程)讲师 2.软件研发管理佳实践顾问(曾任华为某团队研发顾问) 3. 中国敏捷联盟《ADBOK》(敏捷开发知识体系)项目组成员 二十年软件开发、软件设计、需求分析、项目管理、部门管理、公司管理及过程改进等经验,亲历“无数”项目,涉猎建筑、通讯、互联网、电力、金融、制造业、政府等领域,熟悉软件生命周期的全部过程

    10404 人正在学习 去看看 CSDN讲师

测试驱动开发 TDD(Test-Driven Development)是敏捷开发的一项核心实践,同时也是一种设计技术和方法。

  • 既然是测试驱动,便是测试,测试用例先行;
    • 首先编写好测试用例,期待值,实际值;
  • 开发的目的是让测试运行通过;
  • 开发围绕测试展开;

0. 常见测试方法

  • 功能测试、单元测试、系统测试和负荷测试等;

1. 基本思想

在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。

2015-05-20 16:14:21 jiaoyang623 阅读数 1022
  • SCRUM敏捷开发视频教程

    SCRUM敏捷开发视频教程,该课程为你分享SCRUM敏捷开发,理解敏捷的本质,认识中国IT行业对敏捷的挑战,学会让敏捷落地的实用招数。 嘉宾介绍:张传波 1. 创新工场创业课程(敏捷课程)讲师 2.软件研发管理佳实践顾问(曾任华为某团队研发顾问) 3. 中国敏捷联盟《ADBOK》(敏捷开发知识体系)项目组成员 二十年软件开发、软件设计、需求分析、项目管理、部门管理、公司管理及过程改进等经验,亲历“无数”项目,涉猎建筑、通讯、互联网、电力、金融、制造业、政府等领域,熟悉软件生命周期的全部过程

    10404 人正在学习 去看看 CSDN讲师

测试驱动开发相关资料在上大学的时候就读过,一直没用上。最近不知不觉又把这个东西捡起来,大概是下面这个样子

TDD

  1. 敏捷开发。毕业之后渐渐发现传统软件开发模型不太适用,而敏捷开发方法更适用于互联网公司这样需求多变、明天未知的场景。敏捷开发中使用“用户故事”对需求进行描述和讨论,而完成讨论的用户故事是要包含验收条件,即测试条件。在确定需求的时候就确定了测试用例,那下一步就可以编写测试用例了。
  2. 《程序员修炼之道》,学习敏捷的时候找到的一本书。书里提到按照合约设计代码,即先设计好输入条件、输出条件和不变项,再根据这些条件设计代码。显然这样设计的代码更容易编写测试程序。书中提到一个概念,叫依靠巧合开发,大概是这样:
大概了解需求 -> 编写一些代码 -> 跑几个用例 -> 成功了 -> 再跑几个用例 -> 又成功了 -> 提交测试

当了几年码农,基本按照这种方式开发,程序的漏洞交给测试去找,有点low.所以这几天趁着不是项目不是很近的机会小试一把测试区动开发.主要有以下几点感想:

  1. 作为开发者考虑的时候,想的是如何让代码跑起来
  2. 作为测试者考虑的时候,很自然就会去找各种特例状况,比如正常值中的不同分类/边界值/错误值和并发性等问题

不过说实话,不是所有代码都能拿测试驱动开发来测的,给用户界面编写测试用例是一件很疯狂的事儿.但是如果我们的程序是按照MVC结构设计的,对M和C层进行测试到没那么麻烦.

后面说说Android中应用JUnit进行单元测试遇到的一些问题:

  • 用命令去创建测试工程:
    • android create test-project -p . -m ../Bravo -n BravoTest
    • 创建时需要在测试工程的文件夹下执行,也就是事先创建好测试文件夹,并进入
    • -p 是测试项目路径
    • -m 是目标的测试工程
    • -n 是测试项目的名称
  • Android的JUnit,需要在方法前面添加test前缀,系统才能识别这个方法是测试方法.@test标签这里不管用
  • 出现引用其他工程的时候,需要将测试项目的dependency中scope设置成private
  • 线程回调的方式采用wait()加notify()方式来做,可将多线程变成单线程
    • 启动线程的地方写:synchronized(lock){lock.wait();}
    • 回调地方写:synchronized(lock){lock.notify();}
  • TestCase中,每写一个test方法,这个类就会新建一个实例,所以测试一个类的某个流程,还是用一个test方法来写吧

希望对你有用

2011-08-15 15:18:04 test4ever 阅读数 1502
  • SCRUM敏捷开发视频教程

    SCRUM敏捷开发视频教程,该课程为你分享SCRUM敏捷开发,理解敏捷的本质,认识中国IT行业对敏捷的挑战,学会让敏捷落地的实用招数。 嘉宾介绍:张传波 1. 创新工场创业课程(敏捷课程)讲师 2.软件研发管理佳实践顾问(曾任华为某团队研发顾问) 3. 中国敏捷联盟《ADBOK》(敏捷开发知识体系)项目组成员 二十年软件开发、软件设计、需求分析、项目管理、部门管理、公司管理及过程改进等经验,亲历“无数”项目,涉猎建筑、通讯、互联网、电力、金融、制造业、政府等领域,熟悉软件生命周期的全部过程

    10404 人正在学习 去看看 CSDN讲师

关于测试驱动开发的思考

         测试驱动开发(Test Driven Develop)作为敏捷思想的重要组成部分,将开发和测试在同一时段完成,我认为是一个很不错的想法,尤其是经历了无数测试后的返工以及开发中的疏漏后,测试驱动开发将作为以后开发工作中首当其冲的选择。

         据我所知,这里有几个误区需要纠正,其一:敏捷开发的宗旨是高效和高质量,降低开发成本,它是有很多前提条件的,其中之一就是开发人员对敏捷开发的认识水平,所以并不是说敏捷就一定更优,如果能够通过传统方式达到相同的目的(明显传统开发有过多的理性行为假设,需要太多的改进),一样是可行的;

其二,测试驱动开发并不是去准备无处不在的测试用例,不是说覆盖所有的理论可能分支才算是测试,测试用例是为了无法通过测试而制定的,将测试人员的心态结合到开发人员中,是非常重要的优势;

其三,文档是记录方式,并不是交流方式,当文档成为交流方式时,要重新考虑团队的开发状况以及沟通成本;

其四,编写测试用例很容易让开发人员焦躁,从下往上的测试用例的编写,对很多人来说可能是一个灾难,尤其在无法正确估计将来可能所带来的成本时,PM很难说服开发人员去采用TDD的方式。这时候增加测试用例的优先级,合理安排sprint显的尤其重要。对于开发人员来说,一次从原则上很简单的返工往往是毁灭性的,违背了的DRY的原则,让他们认识到这一点后,去重新理解TDD的价值,至关重要。

其五,如第四点所说,从下往上的测试用例的编写对有些人是不适用的,按照用户故事细分,为测试用例区分优先级别,制定更加有针对的测试用例,将会达到事半功倍的效果。

 

         关于UI/UX的测试驱动开发方法,界面迁移的测试代码量往往是重复而且冗余的,很多时候没有太大的意义,UI的测试方法往往通过数据跟踪以及用户反馈来进行评估。测试开发的增量更新有些时候非常不适用,对于UI/UX的设计开发方法,一开始的建模十分重要,,如何迁移是一个方面,实际上迁移所带来的问题是测试驱动开发针对UI设计的关键步骤,举例说明,如果该界面增加一个功能点,通过该功能点进行界面迁移的时,设计最不想看到的情形是什么样的。

        

         利用敏捷开发重新认识工作成本的评估,是十分重要的,IT行业工作量难以衡量并不能成为普遍加班的借口。


没有更多推荐了,返回首页