这个争执发生在测试的时候。
背景是这样的:
一个接口有很多乱七八糟的业务相关的方法,其中有这么四个方法:
interface TaxLawBuckets {
double getRemaining401k();
double getRemaining403g();
void apply401k(double amount);
void apply403g(double amount);
}
当然,这个设计有点不是很好。更好的也许是:
interface TaxLawBuckets {
TaxLawBucket get401k();
TaxLawBucket get403g();
}
interface TaxLawBucket {
double getRemaining();
void apply(double amount);
}
不过,因为种种原因,无法采用这个设计(主要是这么一改,改动会比较大,这个系统里面有一个wsdl代码生成器,这个生成器对接口长的样子有变态的要求)。所以就凑合着把。
现在要制作一个PerAccountTaxLawBuckets类,这个类会对401k和403g有额外的逻辑处理:
interface TaxLawBuckets {
private double bucket_401k;
private double bucket_403g;
private TaxLawBuckets globalBuckets;
public double getRemaining401k(){
return Math.min(bucket_401k, globalBuckets.getRemaining401k());
}
public double getRemaining403g() {
return Math.min(bucket_403g, globalBuckets.getRemaining403g());
}
public void apply401k(double amount) {
bucket_401k -= amount;
if(bucket_401k<0) throw new SystemException(...);
globalBuckets.apply401k(amount);
}
public void apply403g(double amount) {
bucket_403g -= amount;
if(bucket_403g<0) throw new SystemException(...);
globalBuckets.apply403g(amount);
}
//所有其他的函数都直接委托给globalBuckets。
}
恩。有些代码重复。主要还是因为这个接口设计的不好。好在重复不多,凑合吧。
下面要写测试代码了。先看怎么写针对401k的代码。pair用的是jmock。
public class PerAccountTaxLawBucketsTest extends MockObjectTestCase {
Mock bucketsMock = mock(TaxLawBuckets.class);
TaxLawBuckets globalBuckets = bucketMock.proxy();
PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
public void testGetRemaining401kReturnsTheMinimal() {
bucketsMock.expects(once()).method("getRemaining401k").will(return(200));
assertEquals(100, perAccount.getRemaining401k());
}
...
}
其它的测试也都是通过jmock设置expectation,然后从perAccount对象调用对应的函数。
下面注意到401k和403g的逻辑几乎一模一样,只有方法名不同。所以为了避免代码重复,我和pair决定用一个abstract class来抽出共性。然后用两个子类来代表不同之处。pair的代码是这样:
public abstract class AbstractPerAccountTaxLawBucketsTest extends MockObjectTestCase {
Mock bucketsMock = mock(TaxLawBuckets.class);
TaxLawBuckets globalBuckets = bucketMock.proxy();
PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
public void testGetRemainingReturnsTheMinimal() {
bucketsMock.expects(once()).method(getRemainingName()).will(return(200));
assertEquals(100, getRemaining(perAccount));
}
...
abstract String getRemainingName();
abstract double getRemaining(TaxLawBuckets buckets);
abstract String getApplyName();
abstract void apply(TaxLawBuckets buckets, double amount);
...
}
public class PerAccount401kTestCase extends AbstractPerAccountTaxLawBucketsTest {
String getRemainingName() {
return "getRemaining401k";
}
double getRemaining(TaxLawBuckets buckets) {
return buckets.getRemaining401k();
}
String getApplyName() {
return "apply401k";
}
void apply(TaxLawBuckets buckets, double amount) {
buckets.apply401k(amount);
}
}
public class PerAccount403gTestCase extends AbstractPerAccountTaxLawBucketsTest {
String getRemainingName() {
return "getRemaining403g";
}
double getRemaining(TaxLawBuckets buckets) {
return buckets.getRemaining403g();
}
String getApplyName() {
return "apply403g";
}
void apply(TaxLawBuckets buckets, double amount) {
buckets.apply403g(amount);
}
}
而我则不太喜欢getRemainingName()和getRemaining的重复。所以我建议用easymock这样写:
public abstract class AbstractPerAccountTaxLawBucketsTest extends TestCase {
MockControl bucketsMock = MockControl.createControl(TaxLawBuckets.class);
TaxLawBuckets globalBuckets = bucketMock.getMock();
PerAccountTaxLawBuckets perAccount = new PerAccountTaxLawBuckets(100, 100, globalBuckets);
public void testGetRemainingReturnsTheMinimal() {
bucketsMock.expectsAndReturn(getRemaining(globalBuckets), 200);
bucketsMock.replay();
assertEquals(100, getRemaining(perAccount));
bucketsMock.verify();
}
...
abstract double getRemaining(TaxLawBuckets buckets);
abstract void apply(TaxLawBuckets buckets, double amount);
...
}
public class PerAccount401kTestCase extends AbstractPerAccountTaxLawBucketsTest {
double getRemaining(TaxLawBuckets buckets) {
return buckets.getRemaining401k();
}
void apply(TaxLawBuckets buckets, double amount) {
buckets.apply401k(amount);
}
}
public class PerAccount403gTestCase extends AbstractPerAccountTaxLawBucketsTest {
double getRemaining(TaxLawBuckets buckets) {
return buckets.getRemaining403g();
}
void apply(TaxLawBuckets buckets, double amount) {
buckets.apply403g(amount);
}
}
这样,减少了重复,代码感觉更干净。
其实,这个例子简化了问题。实际的那个程序,除了getRemaining(), apply()之外,还有restore()和其他几个方法,也是401k和403g除了方法名字不同其他都一样。
但是,pair一听说用easymock直接就把这个方案枪毙了。pair的观点:
为了保持一致性,我们应该都用jmock。使用easymock会给公司里别的程序员带来理解上的困难。(我很心虚地想,我的几个test case都是用的easymock亚。)
对这个说法,我有点不知道如何说了。我一直以来的观点,都是use the right tool for the right job。
如果工具甲能够做功能1,2,3,4,工具乙能够做功能3,4,5,6。那么我不会为了保持一致性而强迫只用甲或者只用乙。而如果一个公司标准的ComplexTool能够做功能1,2,3,4,5,6,一个业界标准的SimpleTool(比如java.util.HashMap)能做功能1,而我又不需要功能2,3,4,5,6,那么我会选择SimpleTool。
何况,在一个比较强调技术的公司,担心大家不会用easymock或者jmock真的必要么?毕竟不管jmock还是easymock应该都是半个小时就能掌握的冬冬把?
其实,这种关于强调一致性的问题我已经和同事有若干次分歧了。另外一次是:公司里大多用一个内部做的MyPropertyFactory framework来读取class path里面的property文件内容。而我有一次独立开发的一个模块直接使用了ClassLoader.loadResourceAsInputStream()。于是,同事以一致性为理由要求我更改代码来使用MyPropertyFactory。具体细节在下一次disagree里面会解释。
那么,一致性和use the right tool真的是矛盾的么?你怎么在两者之间取得平衡呢?
分享到:
相关推荐
诺曼征服后,英语词汇几乎翻倍,特别是通过引入反映更高生活水平和社会复杂性的法语词汇,如与食物(如sugar、vinegar、boil、fry、roast)、服装(如garment、robe、mantle、gown)、法律(如plaintiff、perjury、...
check the manual that corresponds to your MySQL server version for the right syntax to use near ‘(0) NOT NULL, endTime datetime(0) NOT NULL,avaliableTime int(8) NOT N’ at line 5 今天帮一个朋友调试...
标题“Use the Right Words”可能指的是在编程、文档编写或者技术交流中选择准确的词汇以提高沟通效率和代码可读性的重要概念。在这个主题中,我们将会深入探讨如何在IT行业中选择恰当的语言来表达思想,这包括源码...
用于欧陆词典的英文单词用法词典 ,用于欧陆词典的英文单词用法词典
在描述中提到的"A tool for use with clang to analyze .zip",我们可以推测这可能是为了提升软件开发的安全性和质量。在开发过程中,分析.zip文件可能是为了检查二进制库、资源文件或其他打包数据的完整性。通过...
To download the Kafka UI Tool for your operating system, use the links below. All versions of Kafka Tool come with a bundled JRE with the exception of the Linux version. For Linux, you must have Java ...
MSP-FET430 Flash Emulation Tool (FET) (for Use With IAR v3+) User's Guide
On a good description of how to use the makefile is makefile to learn very good things Makefile的规则: target:prerequisities command target可以是obj文件 ,也可以是目标文件 ,也可以是lable 标签 ...
cmd-comand,use command to debug your code in linux environment,it's a useful tool for linux programmer.you can add stub in your code and run the cmd tool to debug it.
vss工具的主要使用方法! 版本控制是工作组软件开发中的重要方面,它能防止意外的文件丢失、允许反追踪到早期版本、并能对版本进行分支、合并和管理
USE THE RIGHT WORD MODERN GUIDE TO SYNONYMS AND RELATED WORDS lists of antonyms copious cross-references a complete and legible index THE EDITORS OF THE READER'S DISGEST and the Funk & Wagnalls ...
The format facilitates the use of virtual appliances, which are preconfigured virtual machines that package applications with the operating system they require. Because OVF runs on multiple platforms...
MTK平台 音频调试工具使用指南 New_audio_tuning_tool_use_guide v2.1.pdf
特征最终一致性即使在极端情况下也能确保缓存的最终一致性强一致性为应用程序提供强一致的访问防崩溃缓存崩溃的更好解决方案防渗透防雪崩批量查询用法此缓存存储库使用最常见的update DB and then delete cache缓存...
本文实例讲述了mysql报错:MySQL server version for the right syntax to use near type=InnoDB的解决方法。分享给大家供大家参考,具体如下: 一、问题: 工作中使用sql语句建表时,mysql报了如下错误: You have ...
综上所述,BS EN ISO 3696 1995标准是实验室操作的关键参考,涵盖了从水的制备、检测到使用的所有环节,对于保证实验数据的准确性和一致性具有重要意义。而提供的压缩包文件则为用户提供了便捷地访问和应用这一标准...
《MISRA C:2012 Guidelines for the use of the C language in critical systems》是MIRA Limited出版的一本关于C语言在关键系统中使用的指南,适用于汽车工业和其他对软件可靠性有极高要求的领域。MISRA(Motor ...
以上知识点提供了对标题《A Fast Taboo Search Algorithm for the Job Shop Problem》和描述中提到的各个方面深入的理解。涉及的标签“Taboo Search”和“Nowicki”指明了算法的关键技术和主要研究者。此外,从部分...
Helixoft_VSdocman_8.4.6583_for_VS2010-2017 VSdocman - Visual Studio documentation ...VSdocman is an ultimate, fast and easy to use .NET documenting tool for every Visual Studio C# and VB developer.