2010-07-18

[读书笔记] 从 TDD 到 BDD

最近在学 ruby, 也用了 rspec, 传统的 xUnit 是 TDD 指导思想下的产物, 而 rspec 则算是 BDD (Behavior Driven Development) 影响下的产品。

TDD 和 BDD 区别究竟何在呢?

首先是思路上的区别, 传统的 TDD 关注的是接口是否被正确地实现了, 所以通常每个接口有一个对应的单元测试函数。而 BDD 通常以类为单位, 关注一个类是否实现了文档定义的行为

具体从 rspec 的实现上来看, 主要有如下的不同

1. rspec 可以成为设计的一部分 , 你在设计一个类的时候, 一般会先写这个类需要实现哪些功能, 然后在逐个实现需求,
比如 XmlParser, 你会先计划他有三个特性, 支持从字符串初始化, 支持"=="符号, 支持序列化为字符串,
这时候你就可以写下如下的测试代码



# xml_parser_spec.rb

describe "XmlParser" do

  it "should support init with string"

  it "should support =="

  it "should can convert to string"

end



此时运行如下的命令就可以看到你规划的类行为

$ spec -f n xml_parser_spec.rb

XmlParser

  should support init with string (PENDING: Not Yet Implemented)

  should support == (PENDING: Not Yet Implemented)

  should can convert to string (PENDING: Not Yet Implemented)

...



上面的输出可以清晰地看到 XmlParser 需要支持的功能。你不用把这些功能需求记在脑子里, 也不放在你的 TODO 列表里,直接放这儿就行了


2. 使用一个句子来描述你要测试的行为, 而不是简单的重复被测试的接口名, 比如一个典型的测试:
it "should return [Miniblog] with different id" do
其中的 "should return [Miniblog] with different id" 就是这个测试的描述

3. 多级的 context, 便于将类的行为分到多个组, 同时每个组可以有不同的上下文环境(setUp+tearDown), 比如一个 xml parser, 如下所示的测试代码可以把行为分为两组,在遇到合法数据时的行为以及遇到非法数据时的行为

describe XmlParser do
  context "with valid data" do
    before do
      @valid_data = "<a>123</a>"
    end
    ...
  end
  context "with invalid data" do
    before do

      @invalid_data = "<a>123</b>"

    end

    ...

  end  
end

4. 支持自动文档化, 在1里已经可以看到 spec 支持生成一个简略的文档来描述你的对象, 下面是一个比较长的例子用于描述 Douban::Authorize 的功能, 树状的结构把类的功能分到了多个小组


5. 表述更接近自然语言的习惯, 比如 xUnit 通常使用 assertEqual(a, 1), 而 rspec 经常使用 a.should == 1

相对于 TDD 来说, 这些都不是什么革命性的突破。但是思路的转变,能够让你的开发更流畅。

首先你在设计时就可以开始写测试代码, 在开发完成后可以通过 rspec 生成的文档来快速浏览你是否完成了所有特性。这些性质都能让测试驱动开发更容易推广。从我的工作经验来看, 由于种种原因 (比如没有迭代周期, 缺乏 code review 制度, 没有硬指标的覆盖率要求...), 大家的测试代码写得很少。而在 rspec 的帮助下, 直接看 rspec 生成的文档就可以督促大家补全测试。

参考资料

[1] The RSpec Book

2010-07-12

SCRIBEFIRETuDFJ9hOFzSCRIBEFIRE

SCRIBEFIREt3xl3sLqSCRIBEFIRE

2010-05-21

[有问有答] Debian 缩略语

Q: 经常在邮件列表中看见 RFS, 这是什么意思
A: RFS 代表 Request For Sponsor, 当你打好了一个包, 需要人上传时, 一般会发送一封邮件到 debian-mentors@lists.debian.org, 标题一般以 "RFS:" 开头, 这样大家就都知道你是想让人帮你上传包, 其他常用的缩写还有

角色:
  • DPL: Debian Project Leader, 就是 Debian 的总负责人, 每年选举一次, 现在的 DPL 是 Stefano Zacchiroli
  • DD: Debian Developer, Debian 开发者, 有投票权和上传包的权利, 我现在的角色就是 DD
  • DM: Debian Maintainer, 比 Debian 开发者权限低,简单来说只能上传自己维护的包,具体情况请看前面的链接
bug 相关:
  • RFP: Request For Packaging, 我觉得这个包应当加入 Debian, 但是我不想去打包, 所以请求谁来打包
  • ITP: Intent To Package, 我想打这个包, 已经在开始工作了, 大家不要抢, 抢也要先跟我联系
  • RFH: Request For Help, 我忙不过来了, 或者技术能力不足, 需要人帮忙一起维护
  • RFA: Request For Adoption, 这个包我不想继续维护了, 谁想接手
  • O: Orphan, 这个包已经被我放弃了, 谁想接收就接手吧
  • ITA: Intent To Adoption, 我要接手这个包
前面这六个缩写可以说是包的一个生命周期, 其中前 5 个你在运行 "reportbug wnpp" 时就可以看到(wnpp 的意思是 Work-Needing and Prospective Packages), 最后一个是修改别人的 "RFA:" 或 "ITA:" bug 后的新标题。这部分可以参考 wnpp 的首页

其他:
  • FTBFS: Failed To Build From Source, 其实就是编译失败, 经常在 bug 报告中出现
  • NMU: Non-Maintainer Upload, 经常在修改日志出现, 表名修改人员不是这个包的指定维护人员
  • NM Process:  New Maintainer Process, 新维护人员流程, 这个流程是针对申请 Debian 开发人员(DD)的, 但由于 DM 概念的引入, 这个名字现在带来了一些混淆, 估计会改一个名字吧.
  • QA: Quality Assurance, 质量保证, Debian 的 QA 小组主要负责每个包的管理首页, 比如 ibus 的, 还有开发工具来保证 Debian 的质量, 比如 lintian, DEHS, ..., 还有就是维护孤儿包(orphaned package)
  • i18n, l10n, m17n: 分别表示 internationalization (国际化), localization
    (本地化), multilingualization (多语言化), Debian 与这方面相关的内容在 http://www.debian.org/intl/l10n/
  • xxx@l.d.o: xxx@lists.debian.org, Debian 的 maillist, 懒得敲字的时候就变成了 @l.d.o
  • .d.o: .debian.org, 同上, 类似的原因
  • CVE: Common Vulnerabilities and Exposures, 一般在修改日志中出现, 一般表示一个安全漏洞
  • LP: launchpad, Ubuntu 的开发网站, 也是 Ubuntu 的 Bug 中心, 在关闭 Ubuntu 相关bug 时, 用这个来支持对应的 bug 号,比如 "LP: #317443", 就指 https://bugs.launchpad.net/bugs/317443
  • 当然还有很多日常使用的缩略语,比如 IMHO, LOL, 大家就自行维基百科吧

2010-05-20

[有问有答] 如何用 git 来管理你的打包工作

Q: 我看到很多包的 debian/control 里面有 Vcs-Git 和 Vcs-Browser 这样的字段, 这个是什么意思?
A: Vcs 的全称是 Version Control System, 即版本控制系统, 使用这两个字段表明打包工作使用了 Git 来协助管理的, 比如 chmsee 的 debian/control 就有如下的两行:
Vcs-Browser: http://git.debian.org/?p=chinese/chmsee.git
Vcs-Git: git://git.debian.org/git/chinese/chmsee.git
这个表示, 如果你要在线查看打包的情况, 那么就直接访问 http://git.debian.org/?p=chinese/chmsee.git
如果你要下载他的打包工作, 那么就运行 git clone git://git.debian.org/git/chinese/chmsee.git

使用版本管理来协助打包是有几个好处的
  1. 首先是常规版本管理带来的好处, 比如误删了文件可以马上恢复, 在多个机器上工作时可以很方便地同步, 一个复杂的工作切成多步后会更加清晰, 出现错误后能方便追踪
  2. 另外就是方便其他开发人员为你提交补丁, 他的补丁可以直接基于你最新的工作, 而不是基于你发布出去的那个版本, 这样能减少你合并补丁时的工作量
  3. 现在 debian 的包管理工作也在逐步演化为一组人管理一组包, 这样的好处是单个人由于某些原因离开 Debian 时,他的包不至于荒废(看着身边的朋友一个个转投 Apple 的怀抱, 我真得很伤心), 而一组人一起管理包时,版本管理系统就成为必须了
下面我就用 chmsee 来演示一下如何把一个包转为 git 管理
  1. 你要安装一些 git 工具包: sudo apt-get install devscripts git-buildpackage gitk git-gui
  2. 找一个空目录, 然后用如下的命令下载 chmsee 的源码包: dget http://ftp.debian.org/debian/pool/main/c/chmsee/chmsee_1.1.0-1.dsc (如果年代久远, 这个 URL 不再可用, 请到QA页面查找新链接)
  3. 运行 git import-dsc chmsee_1.1.0-1.dsc, 这时会创建出一个新目录: chmsee
  4. 进入 chmsee 目录, 运行 gitk --all, 可以看到已经有两个分支(master 和 upstream), 和两个 tag (upstream/1.1.0 和 debian/1.1.0-1)
  5. 运行 debuild -i.git 编译
日常操作: 发布新版本:
  1. 下载新版本, 比如 chmsee-1.1.1.tar.gz
  2. 在 chmsee 目录运行 git import-orig ../chmsee-1.1.1.tar.gz
  3. 运行 dch -v 1.1.1-1, 加入一行 new upstream release, 保存
  4. 运行 git add debian/changelog
  5. 运行 debcommit
  6. 运行 debuild -i.git 编译
如果你对 git 的操作本身已经非常熟悉, 那么 git-buildpackage 上手也没有什么难度。如果你对 svn 更拿手, 那么可以考虑使用 svn-buildpackage.


2010-05-02

[有问有答] 软件如何进入 Debian

Q: 我有一个软件,我觉得他可以进入 Debian, 那么如何让他进入 Debian 呢?
A: 整个流程分为如下几步:
  1. 首先你应该提交一份 ITP bug, ITP 的含义是"Intent To Packaging", 最简单的方式是运行 "reportbug wnpp", 接着选择 "ITP", 填完他要求的内容, 即可提交, 这个 bug 报告最主要的作用是为了防止重复劳动, 可以追踪谁在负责这个包, 也方便直接联系负责人询问进度, 所以也没有必要太详细, 范例可以参见 ibus 的 ITP bug
  2. 稍后你可以收到一封邮件, 里面应该有这个 bug 对应的号码, 比如 ibus 的 ITP bug 号就是 501106, 这个号码在第3步打包时会用到(放在 debian/changelog 文件中)
  3. 接着就是具体的打包工作了, 这个部分内容比较多,不在这儿说了, 详细情况你可以查看Debian 新维护人员手册(英文版, 简体中文版)。 打好的包首先要保证能用, 同时最好用 pbuilder 编译一遍,确定能编译成功, 用 lintian 检查一遍, 确认没有警告, 然后再复查一下 debian/copyright 里面的版权声明, 是否有遗漏和错误, 这些都检查完毕后, 就可以申请上传了.
  4. 接着呢,你需要把这个包上传到 mentors.debian.net, 方便其他人员检查你的包, 上传方法可以查看他的文档
  5. 接着呢你要发封信到 debian-mentors@lists.debian.org, 找一个Debian Developer 帮你上传, 这个人一般被称之为 sponsor, 这封信的标题一般含有"RFS", 含义为 "Request For Sponsor"。mentors.debian.net 一般已经为你准备了一个模板,  就在你的包详细页面下面会有一个链接, 你把模板拷贝一份, 补充一下里面缺少的内容, 就可以发到 debian-mentors@lists.debian.org , 值得注意的是, 如果你没有订阅这个邮件列表, 记得在邮件里面加上一句 "please cc me, thanks.".
  6. 接着呢就需要等待几天时间, 当然如果运气不好, 没人对你的包没有兴趣的话, 可能需要更长的时间, 一般两周后你可以重新发一遍你的邮件, 补充说明一下为什么你的包值得进入 Debian. 如果你想加快这个进程, 一个办法是到 IRC 问问, IRC 服务器是 irc.debian.org (不是 freenode), 频道是 #debian-mentors 。另外, 如果你有认识的 Debian Developer 的话, 可以直接给他发信, 问他是否愿意帮你上传这个包。
  7. 包上传后就进入 Debian 的 unstable了(即sid), 然后一般会在 10 天后进入 testing (需要满足如下的要求: a. 包在所有的平台上都成功编译, b.这10天内没有发现重大 bug, c. 你所依赖的包也已经都在 testing 存在了), 下一个 stable 版发布时就会包含你的包了。
  8. 恭喜你,你的包已经进入 Debian 了。



2010-05-01

[有问有答] 什么样的软件可以进入 Debian

Q: 什么样的软件可以进入 Debian?
A: 首先是授权协议, 所有进入 Debian 的软件都必须满足 DFSG, 值得注意的是, DFSG 与自由软件的定义有不一致的地方, 比如 cc-by-nc 协议就不是 DFSG 兼容的, 部分 GFDL 文档(含有不可变文本) 也不满足 DFSG, 如果你对软件授权是否满足 DFSG 拿不准, 那么可以到 debian-legal 咨询

剩下的是一些非强制的标准, 但 Debian 开发人员会通过这些来判定一个包是否值得上传:
  • 上游是否仍然在维护这个软件, 这个尽管不是强制标准, 但除非这个软件不可替代(比如 mutt?), 否则一般不会收纳这种软件.
  • 与现在仓库中同类软件相比, 这个软件的特点是什么?这条就比较容易达到, 毕竟哪个软件没有自己的特点呢?功能, 基础库(比如 GTK vs QT), 速度, 大小, 等等都可以是自己软件的特点。
  • 软件质量如何?是否经常崩溃?
  • 软件的用户数多少?几乎没人使用的软件很难保证质量。
  • 如果没有 Debian 打包流程, 用户直接使用是否方便, 比如不依赖于本地库的 firefox 插件就倾向于不要打包, 因为安装起来实在是太方便了
  • 如果你在打包一个库的话, 这个库是否有程序使用? 如果没有程序使用, 那么这是否是一个重要的开发工具包? 如果都不是话, 那么一般不会收纳这种库.
以上其实都不是强制标准, 其实最关键的问题是是否有人愿意贡献时间来打包和进行后续维护, 打包后能否说服 Debian 开发人员帮你上传。Technorati Tags:

2010-02-04

tomcat 部署 https

tomcat 部署 https


  1. 在 startssl 上申请认证, 最后你会得到两个文件: ssl.key 和 ssl.crt, 建立一个新目录, 把这两个文件拷贝到该目录
  2. 下载 https://www.startssl.com/certs/ca.pem 和 https://www.startssl.com/certs/sub.class1.server.ca.pem
  3. 运行
    cat ssl.crt ca.pem sub.class1.server.ca.pem > chain.crt
  4. 运行
    openssl pkcs12 -export -in chain.crt -inkey ssl.key -out keystore.pkcs12 -name tomcat
    使用密码 "PASSWORD"
  5. 运行 (可选, 该步为校验步骤)
    keytool -list -rfc -keystore keystore.pkcs12 -storetype pkcs12
    注意: 结果中要含有如下的一行 "Certificate chain length: 3"
  6. 运行
    keytool -importkeystore -srckeystore keystore.pkcs12 \
    -srcstoretype PKCS12 -destkeystore keystore \
    -srcalias tomcat -destkeypass PASSWORD
    (注意: 此处的 PASSWORD 需要与目标 store 的密码一致) 
  7. 运行
    cp keystore $TOMCAT_HOME/conf/
  8. 修改 $TOMCAT_HOME/conf/server.xml, 去掉 SSL Connector 附近的注释, 内容如下:
    <Connector URIEncoding="UTF-8" port="443" protocol="HTTP/1.1" SSLEnabled="true"
    maxThreads="150" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS"
    keystoreFile="conf/keystore"
    keystorePass="********"
    keyAlias="tomcat"
    />
  9. 把其余的 redirectPort="8443" 改为 redirectPort="443"
  10. 重启 tomcat
  11. 调整防火墙,启用 443 端口
  12. 备份 ssl.key 和 ssl.crt 到安全位置, 删除整个目录, 设置 $TOMCAT/conf/keystore 的属主为 tomcat 的运行用户, 设置权限为 640 或更低权限

注意事项:
  1. ssl.crt ca.pem sub.class1.server.ca.pem 都必须是 PEM 格式 (看起来类似base64)
  2. 所有步骤中建议使用同一套密码
  3. 此处对密码的要求不是很高, 因为运营机器上的 root 用户能同时拿到 keystore 和 server.xml, 密码再复杂也无效, 非 root 用户拿不到 keystore, 所以是安全的
  4. 如果有更高的安全要求, 建议修改 tomcat 的启动脚本, 在启动时手动输入密码, 设置为环境变量, 然后在 server.xml 中使用环境变量来获得密码 (仍然有问题, root 不在自己手上, 安全经常是幻像)
  5. Firefox, Chrome, IE 下均不会报警, 但 Java 客户端仍然不能使用, 解决方法如下
    1. 运行: wget https://www.startssl.com/certs/ca.pem
    2. 运行: keytool -importcert -trustcacerts -file ca.pem -keystore foo -storepass changeit
    3. 把 foo 拷贝到你的程序中
    4. 把如下两行代码加到你的程序 main 函数中:
      System.setProperty("javax.net.ssl.trustStore", "/path/to/foo");
      System.setProperty("javax.net.ssl.trustStorePassword", "changeit");