Maven、Nexus、SVN、Hudson 以及团队异地开发那些事(3)

1      SVN

1.1  SCM常见模式与反模式

一句话总结:不要提交能根据现有文件自动生成的文件。

表3‑1 常见的SVN模式与反模式

不提交能基于代码库自动生成的文件

减少svn库大小,提高checkout时间,加速CI的时间,因此也减少了发现bug的时间

典型的分成如下几类:

1.  与开发工具相关的文件,例如eclipse的.setting文件、.classpath文件、.project文件、自定义class Variable

2.  不会被他们共享的文件,例如不会重复执行的test data相关的resources文件;

3.  历史SCM工具残留文件,例如cvs的残留 .vssc,vssver2.scc文件

提交时必须写日志,日志必须有意义

log的作用:

1.       能发现当初你提交这个版本时的意图的log;

2.       在你需要找到任何一个历史版本的时候,方便你搜索,所以log要类似于关键词的作用,要有区分性

 

典型的log pattern:

1.  修改bugxx,增加了空指针判断;

2.  增加了xx功能,避免xx问题;

3.  增加了xx类的单元测试,测试了xx接口;

4.  提交了测试的resoures文件,避免单元测试重复执行时报错。

5.  修改了xx文件错误的配置。

 

典型的log的anti-pattern:

1.  修改了xxx文件,modified by xxx(svn的log状态就能发现是谁提交时删除还是更新了xx文件)

2.  长期使用last log(日志相同)

3.  在data(其实在maven下应该是test/resources)提交了大量的PDF、word等作为测试文件,但是单元测试代码中仅仅使用到了很少的部分

4.  把SVN当成软件共享服务器,将大文件,如IDE工具、XXX服务器软件、XXX工具软件都作为配置管理项目都提交到svn上。

5.  提交了能通过word文档自动生成的帮助文件脚本

6.  未提交数据库的初始化脚本。

长时间不更新 或者 琐碎的提交

正常的更新节奏:

  1. 每天至少更新的次数 >= 你提交的次数,提交之前先更新,至少提交之后要更新一次,保证你提交的代码跟别人提交的代码没有冲突;
  2. 与其他人在并行开发,更新次数> 无并行开发的部分;
  3. 每天早上来的第一件事情,就是更新代码是个好习惯;
  4. 多更新也没有坏处。

琐碎的提交:

1.  思路不清楚时,将svn当成“垃圾回收站”,任何一次细小的修改都要提交到svn上,因为“指不定什么时候我又会需要它”

 

1.2  不仅仅是SVN,而是整个SCM

需要解决如下问题;

1)        如何保证多条产品线同时开发,并且多条产品线之间还有组件的依赖关系?

2)        如何保证产品在开发新特性的时候,不影响产品的稳定性?不影响其他依赖该产品组件的其他产品的开发?

3)        如何响应不可预知时间的客户发布版本的请求?

4)        内部的测试版本应该如何发布?

5)        对外发布版本应该是谁来做?与对内发布版本流程应该一样吗?

6)        一个产品的发布与几个产品一起打包发布流程应该是一样的吗?一个产品单独发布与几个产品一起打包发布有什么不同?license是逐个产品控制还是每个安装包只做一次?

7)        产品的license控制,是发布时做还是编译时做?

 

“可重现性”(reproducibility)为什么是如此重要?我们需要哪些方面的可重现性?

1)        测试人员发现了一个bug,开发人员确认该bug的确存在。涉及到2个问题:

1)        测试人员是在什么环境下测试的?

2)        包括产品的安装版本、安装的操作系统的环境、出现该bug的页面操作流程、具体的bug表现的现象。

2)        系统上线后,在客户现场出现了问题,客户让派人去现场定位问题:

现场人员需要定位问题,这涉及到如何在运行时进行故障诊断的问题,或者更严重点,系统挂掉了之后,“尸体”是否保存完整的问题——当时出异常的位置和完整的调用堆栈,甚至包括当时的调用的参数等环境信息是否能完整地保存到log日志当中。

1.3  分支模型(策略)

几种常见的分支模型:稳定trunk模型(MainLine Model)、基于trunk开发模型(Trunk Based Development)、短生命周期分支(Short LivedFeature Branches Model)、级联模型(cascade)等。

1.3.1             MainLine Model

图3‑1 主干模型

推荐选用,具有如下特点:

1)        保持主干稳定,主干的代码经过严格测试,随时可以对外发布;

2)        经过主干打出的分支,分成2种类型:

a)        Release分支,代码也经过严格测试,代码稳定,基于该分支已经向最终客户发布产品了,客户已经在使用了,但是也可能存在小bug。

b)        ProjectX分支:为了验证某种特性,实现某个功能或者为了某个具体项目而打的分支,代码不稳定,甚至没有经过单元测试,作为merge到主干前,开发人员代码的临时存储地。

3)        分支的“可靠程度”,从上到下依次递减。

 

FAQ:

1)        何时打project类型的branch?

先回答这个问题——预计在这个分支上做的修改,是否要持续数周,并且会在完成时一次性全部提交到SVN上吗?如果是,那么就应该打分支。

2)        何时打release类型的分支?

内部计划的产品发布,向测试组发布测试版本;客户临时的茶品发布请求,后者需要评估是否原来的发布的产品能满足客户的要求(是否遗留有严重的bug,客户正好要使用;是否有新功能,该版本并没有包含),如果能,尽量不重新发布新版本。

3)        为什么要分支与主干同步

分支的代码最终是要merge回主干的,分支和主干是并行演化的,长时间不保持与主干同步,待到分支完成时一次性merge回主干,merge时冲突多,甚至是不可能完成的任务。

4)        如何保持分支与主干的同步?

表3‑2 保持分支与主干同步的原则


总之保持如下原则:

1)        经常merge比你当前工作分支更稳定的分支的代码;

2)        永远不要merge比你当前工作分支不稳定的分支的代码;

分解成如下几种情况:

1)        从Release->MainLine,要经常进行;


图3‑2 Rlease到MainLine的merge要经常进行

2)        从MainLine向Release1.x,禁止;


图3‑3 MainLine到Rlease的merge要禁止

3)        从Release1.x向project类型的分支,要经常进行,并且要逐级进行,不能越级;


图3‑4 Rlease到Project的merge要经常进行,并且逐级完成

4)        从project向MainLine,仅在特定的时间点进行,就是“功能完成”的时刻。需要特别注意“完成”标准的定义,必须非常严格,其他时间不允许。


图3‑5 Project到MainLine的merge仅限于在“完成”时进行

1.3.2             Trunk Based Development Model

图3‑6 Trunk-Based开发模式

关键问题在于:主干很难达到release的标准。很可能为了release需要暂停新功能开发。

1.3.3             Short-lived Feature Branch

图3‑7 Short-lived Feature Branch

可以与MainLine模型配合,project类型的分支与Short-lived分支有共同点,关键在分支生命周期长短的定义。

但是随着CI的出现,这种模型越来越不被推荐使用。CI的理念是既然代码是所有人都基于一个分支工作,将保持该分支稳定的工作交给CI。

1.3.4             Cascade Model

图3‑8 Cascade Model

复杂程度较高,有大量的分支之间merge的操作,日常维护工作量较大。一般很少使用。

1.4  SVN服务器细粒度权限控制

当与maven配合时,svn可以进行逐个目录的权限控制,实现模块化并行开发,并且最大限度的进行源代码隔离,例如前台开发不需要看到后台代码,或者由于对知识产权保护,核心代码不对外包人员公开等。以下是一个例子实现trunk下的目录waibao(外包人员) &qiantai(前台开发人员)只能看到docs目录和projectA_web目录,配置要点如下:

1)        在根目录(或者trunk目录)配置2人只读;

2)        在不允许其访问的子目录中,使用*= 让其不能访问(但是需要注意会阻断路径权限继承,需要对一些特殊的用户或者用户组,单独添加进来,例如@admin用户组);

3)        在允许其访问的子目录中,使用waibao= rw &qiantai= rw允许其读写。

 

[projectA:/trunk]

@admin = rw

#此处省略了其他人员配置

waibao= r

qiantai= r

 

[projectA:/trunk/projectA_model]

@admin = rw

#此处省略了其他人员配置

*=

 

[projectA:/trunk/projectA_service]

@admin = rw

#此处省略了其他人员配置

*=

 

[projectA:/trunk/projectA_web]

#此处省略了其他人员配置

waibao= rw

qiantai= rw

 

[projectA:/trunk/docs]

#此处省略了其他人员配置

waibao= rw

qiantai= rw

1.5  FAQ

1)        如何设置必须提交日志?

日志、作者、文件和路径都是可以作为检索条件的,典型的log用途:

a)        作为日后后悔时的索引,方便在多次提交的记录中快速定位某次提交;

b)        可以记录设计思想和项目架构演化的痕迹;

2)        常见的忽略都有哪些?

常见的忽略有:

build.propertiestarget dist *.log *.project *.classpath *.checkstyle

bin .cloverjunit*.properties .settings .fbprefs .pmd lib

.externalToolBuildersmaven-eclipse.xml *.iml .idea

3)        如何设置递归忽略?

2种递归设置svn忽略的方式:

svn propsetsvn:global-ignores '*.o' .

svn propsetsvn:ignore '*.o' . --recursive

 

4)        如何进行代码冻结(freeze)?

使用svnadmin freeze PATH

5)        如何移动代码?

图3‑10 使用TortoiseSVN移动已经versioned的文件夹

右键按住需要被移动的source文件夹,拖动到destination文件夹,然后释放右键,在弹出的菜单上选择第一项。

6)        为什么有些时候更新SVN的Working Copy的时候需要进行clean up?

因为一些原因,有时候会导致一个svn update失败,典型的造成WorkingCopy Locked的操作有:

a)        在update的过程中,手工cancel了update;

b)        在update的过程中,由于网速慢,更新失败;

c)        运行external的diff程序(非tortoiseSVN自带的diff程序)

7)        eclipse kepler安装SVNConnector

参见 http://www.polarion.com/products/svn/subversive/download.php

2      UnitTest

2.1  为什么要有UnitTest

1)        开发人员证明自己的确做到了,也是在出现问题时,将自己的“责任摘干净”的有力工具;

2)        开发人员已然做过这个事情,为什么不作为代码库的一部分提交到SVN呢,让大家都知道你测试过,并且是怎么测试的;

3)        将单元测试看成你自己的“好伙伴”,它时时刻刻守护你的代码,无论是你有意的重构,还是代码被无意的修改,单元测试通过Fail来给你提示;

4)        Learn by Unittest,既为自己,也为第三方开发人员(为什么API doc不行?因为API doc是以单个方法为粒度的,而为了完成一个用户视角的功能,往往需要多个方法进行过程式的调用,而这个如果没有进行用户友好的封装,往往需要调用者自己进行)。

 

2.2  遵循的原则

1)        没有Assert的测试不是单元测试;

2)        单元测试既要有面向内部开发人员的单个方法级别的测试,也需要面向第三方开发人员的业务功能级别的单元测试(有点“集成测试”的含义);

3)        单元测试要写到什么程度为止:方法的边界、正常、异常值都测试到为止,客观的评价指标:分支覆盖、条件覆盖、语句覆盖等,单元测试的覆盖率要达到一定的程度(在以往应用系统的开发中我们后台代码的单元测试覆盖率要达到75%以上),具体标准前台后台标准不一,产品和项目标准也不一样,团队要找到适合自己的阈值。

4)        单元测试要“就近测试”,单元测试代码与项目的主代码应该放到一起,并且一起作为产品代码提交到SCM管理;

5)        单元测试代码和相关的数据库、测试资源文件都必须提交到svn,保证能重复、自动执行。并且一个单元测试不会给其他单元测试(包括其自身)带来任何后续的影响(如一个单元测试执行完成之后,忘了清理数据库,在数据库中遗留了一条记录,下次另一个单元测试又会假定该记录在该表是空的,最典型的是getById(int));

6)        UnitTest中涉及到路径的地方使用相对路径,而不是绝对路径。

7)        针对算法的测试,需要根据算法理论,选取合适的数据集(保证提交到SVN或者保证在测试库中始终存在),验证手工推导的结论与算法实际执行结果是否一致,保证算法的每一步都严格按照预想的执行。不能只测试最后的准确度或者算法是否执行过,没有报错(这个甚至都不能叫“集成测试”)。

8)        功能性的单元测试和性能单元测试要分开(例如使用Junit的category分组),因为实际在CI时,这2种类型的测试最好是分开执行,保证功能性单元测试短时间内快速执行完毕,性能测试往往要长时间运行,几小时甚至几天。

9)   一些减少bug的“闲话”:

常见的null pointer异常与“防御式编程”。开发人员采用谨慎的态度,对函数输入参数,进行合法性检查,保证排除所有异常情况后才执行正常流程。对一切输入都持“怀疑”态度,特别是UI界面的输入和开放给第三方用户调用的API接口。

2.3  maven下单元测试的编码规范

1)        “就近测试”,测试代码跟主代码都提交到一个工程下;

2)        src/main/java文件夹下,package为com.xxxx.yyy下的ClassA的测试类的方法methodA,相应的测试类的package保持不变,位置放置到src/test/java下面,测试类的命令遵循“原类名+Test后缀命名法,命名为ClassATest,测试方法采用“test+原方法名”前缀命名法,命名为testMethodA。

参考文献

[1].    http://paulhammant.com/2013/12/04/what_is_your_branching_model/

[2].    《代码大全》2nd

[3].    http://maven.apache.org

[4].    《maven实战》

[5].    http://www.infoq.com/cn/articles/road-of-automated-unit-testing-practices



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值