??xml version="1.0" encoding="utf-8" standalone="yes"?>免费成人在线电影,а√天堂中文资源在线bt,国内伊人久久久久久网站视频http://www.aygfsteel.com/jiangshachina/category/32066.html同是Java爱好者,盔R何必曾相识Q?lt;br>    a cup of Java, cheers!zh-cnWed, 04 Jan 2012 13:24:06 GMTWed, 04 Jan 2012 13:24:06 GMT60探烦(ch)JUnit4扩展Q深入Rule(?http://www.aygfsteel.com/jiangshachina/archive/2012/01/04/367802.htmlSha JiangSha JiangTue, 03 Jan 2012 16:13:00 GMThttp://www.aygfsteel.com/jiangshachina/archive/2012/01/04/367802.htmlhttp://www.aygfsteel.com/jiangshachina/comments/367802.htmlhttp://www.aygfsteel.com/jiangshachina/archive/2012/01/04/367802.html#Feedback0http://www.aygfsteel.com/jiangshachina/comments/commentRss/367802.htmlhttp://www.aygfsteel.com/jiangshachina/services/trackbacks/367802.html
探烦(ch)JUnit4扩展Q深入Rule
本文?探烦(ch)JUnit4扩展"pd中的W三,进一步探IRule的应用,展示如何使用Rule来替代@BeforeClassQ@AfterClassQ@Before和@After的功能?2012.01.04最后更?

在本pd的第二篇《探索JUnit4扩展Q应用Rule?/a>中提刎ͼ可以使用Rule替代现有的大部分Runner扩展Q而且也不提倡对Runner中的withBefores()QwithAfters(){方法进行扩展。本文将介绍如何使用Ruled现@BeforeQ@After和@BeforeClass的相同功能?/span>

1. BaseRule
    首先要创Z个较通用的TestRule实现BaseRuleQ它?x)释攑և两个扩展点,一个在执行试Ҏ(gu)之前Qbefore()Q另一个在执行试Ҏ(gu)之后after()。下面是该类的代码,
public abstract class BaseRule implements TestRule {

    @Override
    
public Statement apply(Statement base, Description description) {
        
return new RuleStatement(base, description);
    }

    
private class RuleStatement extends Statement {

        
private Statement base = null;

        
private Description description = null;

        
private RuleStatement(Statement base, Description description) {
            
this.base = base;
            
this.description = description;
        }

        @Override
        
public void evaluate() throws Throwable {
            before(base, description);
            
try {
                base.evaluate();
            } 
finally {
                after(base, description);
            }
        }
    }

    
protected void before(Statement base, Description description) throws Throwable {

    }

    
protected void after(Statement base, Description description) {

    }
}
如果对JUnit4的源代码略有认知Q可能会(x)发现BaseRule与JUnit4提供的TestRule实现ExternalResource代码怼。关键的不同之处是,BaseRule中的before()与after()Ҏ(gu)都提供了(jin)Statement与Descriptioncd的参敎ͼq得它能够完成更复杂的工作?/span>

2. CalculatorTest
    本文使用的CalculatorTest不使用@BeforeClassQ@Before和@AfterQ而会(x)创徏两个BaseRule的实例:(x)一个用于替代@BeforeClass和@AfterClass(本系列目前还未用过@AfterClass)Q另一个则替代@Before和@After?/span>
public class CalculatorTest {

    
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss_SSS");

    
private static Calculator calculator = null;

    @ClassRule
    
public static BaseRule classRule = new BaseRule() {

        
protected void before(Statement base, Description description) throws Throwable {
            calculator 
= new Calculator();
        };
    };

    @Rule
    
public BaseRule rule = new BaseRule() {

        
protected void before(Statement base, Description description) throws Throwable {
            printBeforeLog(description);
        };

        
protected void after(Statement base, Description description) {
            printAfterLog(description);
        };

        
private void printBeforeLog(Description description) {
            TestLogger testLogger 
= description.getAnnotation(TestLogger.class);
            
if (testLogger != null) {
                StringBuilder log 
= new StringBuilder(format.format(new Date()));
                log.append(
" ").append(description.getClassName()).append("#")
                        .append(description.getMethodName()).append(
"")
                        .append(testLogger.log());
                System.out.println(log.toString());
            }
        }

        
private void printAfterLog(Description description) {
            StringBuilder log 
= new StringBuilder(format.format(new Date()));
            log.append(
" ").append(description.getClassName()).append("#")
                    .append(description.getMethodName()).append(
" end");
            System.out.println(log.toString());
        }
    };

    @Test
    @TestLogger(log 
= "a simple division")
    
public void simpleDivide() {
        
int value = calculator.divide(82);
        Assert.assertTrue(value 
== 4);
    }

    @Test(expected 
= ArithmeticException.class)
    @TestLogger(log 
= "divided by zero, and an ArithmeticException thrown.")
    
public void dividedByZero() {
        calculator.divide(
80);
    }
}
值得注意的是QclassRule是静(rn)态变量,它用@ClassRule AnnotationQ将替代@BeforeClass和@AfterClassQ而rule是成员变量,它用@Rule AnnotationQ将替代@Before和@After。与
之前文章不同的是Q此处不仅会(x)在执行测试方法之前打印指定内容的日志(printBeforeLog())Q还?x)在执行试?gu)之后打印一条固定格式的日志(printAfterLog())Q用于指C试Ҏ(gu)已经执行完毕?jin)?/span>

3. 结
    使用Rule可以替代l大部分的Runner扩展Q而且特定的Rule实现可以被复用,也易于添加或U除Rule实例Q这些都大大地提高了(jin)灉|性。值得注意地是Q本文虽然用Rule代替?jin)@BeforeClassQ@AfterClassQ@Before和@After的功能,但ƈ不意味着应当这么做。就我个人所惻I传l的Fixture功能交由@BeforeClassQ@AfterClassQ@Before和@After实现Q仍然是一U不错的选择?/span>


Sha Jiang 2012-01-04 00:13 发表评论
]]>
探烦(ch)JUnit4扩展Q应用Rule(?http://www.aygfsteel.com/jiangshachina/archive/2011/12/24/366801.htmlSha JiangSha JiangSat, 24 Dec 2011 15:26:00 GMThttp://www.aygfsteel.com/jiangshachina/archive/2011/12/24/366801.htmlhttp://www.aygfsteel.com/jiangshachina/comments/366801.htmlhttp://www.aygfsteel.com/jiangshachina/archive/2011/12/24/366801.html#Feedback0http://www.aygfsteel.com/jiangshachina/comments/commentRss/366801.htmlhttp://www.aygfsteel.com/jiangshachina/services/trackbacks/366801.html
探烦(ch)JUnit4扩展Q用Rule
在上一文?a href="http://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.html">《探索JUnit4扩展Q扩展Runner?/a>中,讨论?jin)一U扩展JUnit4的方式,卻I直接修改Test Runner的实?BlockJUnit4ClassRunner)。但q种Ҏ(gu)昄不便于灵zdd或删除扩展功能。本文将使用JUnit4.7才开始引入的扩展方式--Rule来实现相同的扩展功能?2010.12.25最后更?

1. Rule
Rule是JUnit4.7才开始提供的一U扩展方式,它能够替代大部分已有的Runner扩展。JUnit包含两种Rule AnnotationQ@ClassRule与@Rule。@ClassRule应用于测试类中的?rn)态变量,而@Rule应用于成员变量;相同地是Q这些变量必LTestRule接口的实例,且访问修饰符必须为public?br />?a href="http://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.html">上篇博文中,对BlockJUnit4ClassRunnerq行?jin)扩展,被扩展的?gu)是methodBlockQ现在我们来看看该方法体中的代码Q?br />
protected Statement methodBlock(FrameworkMethod method) {
    Object test;
    
try {
        test
= new ReflectiveCallable() {
            @Override
            
protected Object runReflectiveCall() throws Throwable {
                
return createTest();
            }
        }.run();
    } 
catch (Throwable e) {
        
return new Fail(e);
    }

    Statement statement
= methodInvoker(method, test);
    statement
= possiblyExpectingExceptions(method, test, statement);
    statement
= withPotentialTimeout(method, test, statement);
    statement
= withBefores(method, test, statement);
    statement
= withAfters(method, test, statement);
    statement
= withRules(method, test, statement);
    
return statement;
}
但在BlockJUnit4ClassRunner中,possiblyExpectingExceptions()QwithPotentialTimeout()QwithBefores()和withAfters()都已l被标注ӞJUnit使用Rule来替代这些方法的功能?br />
2. TestLogRule
如第1节所qͼRule Annotation要作用于TestRule接口的实例,那么p先创Z个TestRule的实现类?br />
public class TestLogRule implements TestRule {

    
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss_SSS");

    @Override
    
public Statement apply(Statement base, Description description) {
        TestLogger testLogger 
= description.getAnnotation(TestLogger.class);
        
if (testLogger != null) {
            StringBuilder log 
= new StringBuilder(format.format(new Date()));
            log.append(
" ").append(description.getClassName()).append("#")
                    .append(description.getMethodName()).append(
"")
                    .append(testLogger.log());
        System.out.println(log.toString());
    }

        
return base;
    }
}
如上所C,TestLogRule?a href="http://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.html">上篇博文中的LoggedRunner的代码有许多相同之处Q功能则都是打印出指定的日志Q每行日志又以当时的执行旉与完整方法名作ؓ(f)前缀?br />
3. 使用Rule的CalculatorTest
下面是新的测试类CalculatorTestQ它?yu)不使用BlockJUnit4ClassRunner的扩展LoggedRunner作ؓ(f)试执行器,所以该cL有用@RunWith(LoggedRunner.class)Q那么在执行该测试类时仍然会(x)使用BlockJUnit4ClassRunner?br />
public class CalculatorTest {

    
private static Calculator calculator = null;

    @Rule
    
public TestLogRule testLogRule = new TestLogRule();

    @BeforeClass
    
public static void createCalculator() {
        calculator 
= new Calculator();
    }

    @Test
    @TestLogger(log 
= "a simple division")
    
public void simpleDivide() {
        
int value = calculator.divide(82);
        Assert.assertTrue(value 
== 4);
    }

    @Test(expected 
= ArithmeticException.class)
    @TestLogger(log 
= "divided by zero, and an ArithmeticException thrown.")
    
public void dividedByZero() {
        calculator.divide(
80);
    }
}
?a href="http://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.html">上篇博文中的CalculatorTest相比Q本文中的CalculatorTest除了(jin)没有使用LoggedRunner之外Q还多了(jin)两行代码
@Rule
public TestLogRule testLogRule = new TestLogRule();
在执行单元测试方法之前,BlockJUnit4ClassRunner?x)调用TestRule/TestLogRule中的apply()Ҏ(gu)Q即Q会(x)先打印出日志内容?br />
4. 结
使用Rule对JUnitq行扩展Q能够避免对默认Runner的扩展,为测试类d或移除Rule十分方便Q而且Rule实现cLw也能很方便地被复用。在下一博?/a>中将q一步探索Rule的应用?/div>

Sha Jiang 2011-12-24 23:26 发表评论
]]>
探烦(ch)JUnit4扩展Q扩展Runner(?http://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.htmlSha JiangSha JiangTue, 13 Dec 2011 16:01:00 GMThttp://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.htmlhttp://www.aygfsteel.com/jiangshachina/comments/366289.htmlhttp://www.aygfsteel.com/jiangshachina/archive/2011/12/14/366289.html#Feedback4http://www.aygfsteel.com/jiangshachina/comments/commentRss/366289.htmlhttp://www.aygfsteel.com/jiangshachina/services/trackbacks/366289.html
探烦(ch)JUnit4扩展Q扩展Runner
在用JUnit的过E中Q大家可能会(x)对JUnitq行一些扩展。本文中的示例ؓ(f)JUnit4定义?jin)一个新的AnnotationQƈ相应地对已有的Runnerq行扩展Q其能够解析新引入的Annotation?2011.12.25最后更?

本文臆造了(jin)一个示例,?x)在执行单元试?gu)之前Q自动地为单元测试方法打印日志。该CZ?x)?f)JUnit定义一个新的Annotation用于指定要打印的日志内容Qƈ对JUnit默认提供的Runner实现BlockJUnit4ClassRunnerq行扩展Q其能够识别这个新的Annotation?br />
1. 定义Annotation
    TestLogger是一个作用于Ҏ(gu)的AnnotationQ它只有一个属性,用于指定日志的内容,其代码如下所C,
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestLogger {
    
public String log() default "";
}

2. 扩展Runner
    JUnit提供?jin)若q个Runner的实玎ͼ如BlockJUnit4ClassRunnerQSuiteQ其中BlockJUnit4ClassRunner用来执行单个试用例cRLoggedRunner扩展BlockJUnit4ClassRunnerQ覆写其中的methodBlock()Ҏ(gu)。新的methodBlock()Ҏ(gu)?x)在一开始试图获取被执行试Ҏ(gu)中的TestLogger AnnotationQ如果存在的话,׃(x)打印出指定的日志Q每行日志以当时的执行时间与完整Ҏ(gu)名作为前~。该cȝ代码如下所C,
public class LoggedRunner extends BlockJUnit4ClassRunner {

    
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss_SSS");

    
public LoggedRunner(Class<?> klass) throws InitializationError {
        
super(klass);
    }

    @Override
    
protected Statement methodBlock(FrameworkMethod method) {
        Method classMethod 
= method.getMethod();
        TestLogger loggerAnnotation 
= classMethod.getAnnotation(TestLogger.class);
        
if (loggerAnnotation != null) {
            StringBuilder log 
= new StringBuilder(format.format(new Date()));
            log.append(
" ").append(classMethod.getDeclaringClass().getName())
                    .append(
"#").append(classMethod.getName()).append("")
                    .append(loggerAnnotation.log());
            System.out.println(log.toString());
        }

        
return super.methodBlock(method);
    }
}

3. 应用E序
    Calculator是一个简单的应用E序Q其中定义了(jin)一个除法方法,代码如下所C,
public class Calculator {

    
public int divide(int a, int b) {
        
return a / b;
    }
}

4. 单元试E序
    CalculatorTest是一个简单的单元试E序Q它?x)用两U方式对Calculator中的divide()Ҏ(gu)q行单元试。其代码如下所C,
@RunWith(LoggedRunner.class)
public class CalculatorTest {

    
private static Calculator calculator = null;

    @BeforeClass
    
public static void createCalculator() {
        calculator 
= new Calculator();
    }

    @Test
    @TestLogger(log 
= "a simple division.")
    
public void simpleDivide() {
        
int value = calculator.divide(82);
        Assert.assertTrue(value 
== 4);
    }

    @Test(expected 
= ArithmeticException.class)
    @TestLogger(log 
= "divided by zero, and an ArithmeticException thrown.")
    
public void dividedByZero() {
        calculator.divide(
80);
    }
}

值得注意的是QCalculatorTest特别指定LoggedRunner作ؓ(f)试执行?@RunWith(LoggedRunner.class))Q同Ӟ每个单元试Ҏ(gu)QsimpleDivide()与dividedByZero()Q都使用?jin)Annotation TestLoggerQؓ(f)其指定日志内宏V当执行上述单元试Ӟ?x)自动地打印出如下Ş式的日志内容Q?br />
2011-12-13_23:48:38_218 test.CalculatorTest#simpleDivide: a simple division
2011-12-13_23:48:38_218 test.CalculatorTest#dividedByZero: divided by zero, and an ArithmeticException thrown.

5. 结
通过对BlockJUnit4ClassRunner的扩展,可以让JUnit在运行测试用例时做一些额外的工作。但q种直接修改默认Test Runner的方式ƈ不被提倡,?a href="http://www.aygfsteel.com/jiangshachina/archive/2011/12/24/366801.html">下一文?/a>中将?x)介l用Test Rule来达到相同的扩展目的?br /> 

Sha Jiang 2011-12-14 00:01 发表评论
]]>
~写好的面向对象代码(?http://www.aygfsteel.com/jiangshachina/archive/2008/10/07/232942.htmlSha JiangSha JiangTue, 07 Oct 2008 09:06:00 GMThttp://www.aygfsteel.com/jiangshachina/archive/2008/10/07/232942.htmlhttp://www.aygfsteel.com/jiangshachina/comments/232942.htmlhttp://www.aygfsteel.com/jiangshachina/archive/2008/10/07/232942.html#Feedback7http://www.aygfsteel.com/jiangshachina/comments/commentRss/232942.htmlhttp://www.aygfsteel.com/jiangshachina/services/trackbacks/232942.html~写好的面向对象代码

    本文?a >java.net上的一?a >博客Q作?a >Curtis Cooley对编写好的面向对象代码有些徏议,希望对大安有所帮助?2008.10.08最后更?

获取l验没有捷径。编写好的面向对象代码需要经验,但这儿有三种做法能帮你在一开始就很顺利,即便你是老顽固:(x)
    1. 使用试驱动开?TDD)~写你所有的代码
    2. 遵@单法?/a>
    3. 告之而非问之

使用TDD~写所有代?/strong>
    ?a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/tdd-is-design-activity.html">试先行
~写的代码与按测试后行编写的代码是极Z同的代码。按试先行~写的代码是松耦合与高聚合的。当某个属性或U有Ҏ(gu)需要暴露给试E序Ӟ按测试后行编写的代码怼(x)打破装Q因cdƈ不是Z(jin)试而设计的。如果你首先~写试代码Q你的依赖将?x)更好,你的代码是松耦合与高聚合的。后面会(x)有更多关于测试能帮助你设计更佳代码的内容?br />
遵@单法?/strong>
    代码是简单的Q只要当它:(x)
    1. 执行?jin)所有的试
    2. 不包含重?br />     3. 表达?jin)所有的意图
    4. 使用最的cdҎ(gu)
注意到我用的是个被排序了(jin)的列表是很重要的。顺序是重要的。只有一个main()Ҏ(gu)的的GodClass[1]不会(x)是简单的。这个类可能执行?jin)所有的试Q但在Q何比"Hello, world!"更复杂的E序中,它肯定包含了(jin)重复Qƈ且也没有表达出全部的意图?br /> 我努力用简单法则去xIf问题。我不知道如何用简单法则去L某h~写重量U的If代码。有人可能会(x)提出不同意见Q我也尝试过Q但q样的重量If代码实无法表达意图。但当你阅读如下代码?/span>

if (mobile.getType() == MobileTypes.STANDARD) {
  alert();
}
实难以看出其中的意图。这些代码无论处于哪个方法的上下文环境中Q我们都能知道,如果mobile是STANDARDcd的话Q那么就报警。而你所需要的更多意图呢?
我还有一点儿灉|昄。如果有那样的代码,那么在其它地方肯定还?x)有更多那样的代码。这些代码可能就像:(x)
if (mobile.getType() == MobileTypes.GAS) {
  registerGasReading();
}
?/span>
if (mobile.getType() == MobileTypes.TEXT) {
  sendTextMessage();
}
?/span>
if (mobile.getType() == MobileTypes.LOCATION) {
  notifyLocation();
}
你看出来?jin)吗Q我是看出来?jin)。它q反?jin)规?Q有很多地方都违反了(jin)规则2Qƈ且是一U最坏的情Ş。这D代码有多处重复。重复将极难发现。所以,请帮助防止这U情形的发生Q我已包含其中了(jin)?br />
告之而非问之
a之,告之而非问之意指不要先问一个对象的状态,然后才让它去工作。而应该告之对象如何去工作。这意味着之前所有的那些If例子应该变ؓ(f)Q?/span>
mobile.alert();
?/span>
mobile.registerGasReading();
?/span>
mobile.sendTextMessage();
?/span>
mobile.notifyLocation();
现假N布该E序中的一些If语句块有重复的实现。在"重量UIf"版本的程序中Q可能很隑֏现它们;但在"告之而非问之"版本的程序中Q所有的实现都在Mobile中。所有的实现都在一处,q就便于察觉q根除问题?br />     們֐你的试E序也能帮助你保持代码的z?/span>
public interface Alarm {
  
void alert(Mobile mobile);
}

public class Siren implements Alarm {
  
public void alert(Mobile mobile) {
    
if (mobile.getType == MobileTypes.STANDARD) {
      soundSiren();
    }
  }
}

public class TestSiren extends TestCase {
  
public void test_alert() {
    LocationMobile mobile 
= new LocationMobile();
    Siren siren 
= new Siren();
    siren.alert(mobile);
    
assert(sirenSounded());
  }
}
如果你密切地們֐试E序Q它可能?x)问你?Z么你需要一个LocationMobileL试Siren呢?"的确Qؓ(f)什么呢Q看hQSiren应该q不知道LocationMobile吧?/span>
public class LocationMobile {
  
private Alarm alarm;
  
public LocationMobile(Alarm alarm) {
    
this.alarm = alarm;
  }
  
public void alert() {
    alarm.alert(); 
// alert on Alarm no longer needs a mobile
  }
}

public class TestLocationMobile() extends TestCase {
  
public void test_alert() {
    Alarm alarm 
= EasyMock.createMock(Alarm.class);
    alarm.alert();
    EasyMock.replay(alarm);
    Mobile mobile 
= new LocationMobile(alarm);
    mobile.alert();
    EasyMock.verify(alarm);
}
好像我只是交换了(jin)依赖关系。Alarm不再依赖MobileQ现在是Mobile依赖Alarm。但如果你仔l地观察q个试E序Q你?x)发现真正的依赖关系是,Siren知晓?jin)LocationMobile。一个具体类依赖另一个具体类Q这q反?a >依赖反{原则(DIP)。第二个例子pLocationMobile依赖Alarm接口。具体类依赖抽象Q这满DIP?jin)?br />     如果你用TDDQƈ遵@单法则和告之而非问之原则ȝ写所有的代码Q你处于成Z个更好的面向对象E序员的道\上了(jin)。好的面向对象代码易于阅dl护Q但难以~写Q至,在开始时是这L(fng)。你写的多Q你׃(x)变得好Q也?x)获得更多的l验。同Ӟq些实践l验也会(x)使你在自q道\上受益匪?br />
译注
[1]GodClass(上帝c?指包含了(jin)太多内容的类?br />


Sha Jiang 2008-10-07 17:06 发表评论
]]>
何时~写单元试Q??http://www.aygfsteel.com/jiangshachina/archive/2008/06/09/206812.htmlSha JiangSha JiangMon, 09 Jun 2008 12:55:00 GMThttp://www.aygfsteel.com/jiangshachina/archive/2008/06/09/206812.htmlhttp://www.aygfsteel.com/jiangshachina/comments/206812.htmlhttp://www.aygfsteel.com/jiangshachina/archive/2008/06/09/206812.html#Feedback2http://www.aygfsteel.com/jiangshachina/comments/commentRss/206812.htmlhttp://www.aygfsteel.com/jiangshachina/services/trackbacks/206812.html何时~写单元试Q?/span>
    是在~写一个方法之前就~写它的单元试Q还是在写完q个Ҏ(gu)Q甚x整个cM后才~写单元试呢?John Ferguson Smart[1]在他?a >blog中再ơ提Z(jin)q个问题QƈҎ(gu)自己的经验给Z(jin)一些徏议?2008.06.10最后更?

    都别书生意气?jin)。在你编写一个方法之前或是之后编写单元测?-Ҏ(gu)我的l验Q只要你在编写代码的几乎同时p虑q编写单元测试程序,那么q就无关紧要?jin)。过后再q回?或者根本就不回?写测试程序将D问题。就我个言Q我喜欢在编写少量代码之前或紧接着的之后就~写单元试--q不?x)打破工作流E,因ؓ(f)它就是流E的一部分?br />     q需要一点儿实践l验--~Zl验的开发者经ؓ(f)要写什么样的测试程序而烦(ch)恹{但q可能也反映Z个事实:(x)他们同样也不知道要写什么样的代码。一些h评说TDD能够鼓励q行微设?-一U非常底层的设计Q它不需要考虑较大的场景。这?x)发生在~Zl验的开发者n上;如果你教条般地应用这U方法,同样也会(x)遇上。像行ؓ(f)驱动开发这L(fng)Ҏ(gu)在此处就?x)很酗当你在写getterҎ(gu)之前Q你?x)写一个针对这个getterҎ(gu)的单元测试吗Q如果是的话Q那么你的单元测试专注的层次p高了(jin)Q也?x)更接近于用?或系l?的需求?br />     回到问题的本质,Z么我喜欢把单元测试放在最开始的位置Q很单!我的实践l验告诉我,那样可以帮助提高代码的质量,q且节约调试旉。在开始时写十个小的单元测试所q旉比在以后修复Bug所q旉要少Q如果代码经q了(jin)正确的单元测试,那就不会(x)有Bug?jin)?br />     事实上,我屡屡见刎ͼ如果某些代码l过?jin)适当的单元测试,那么׃?x)有~码问题。最q就有一个例子:(x)׃(jin)一个小时的旉L寻Web应用中的一个问题,该问题出现在一个编写正的Spring-MVCE序中。结果是׃一个检验器cd略了(jin)一个异常。很Ҏ(gu)发C(jin)q个问题Q实际上Q在看了(jin)代码(代码(g)?Code Review)也很有效)之后立刻发C(jin)。但关键是,我们׃(jin)一个小时甚x多的旉Lq个需要进行检查的cR如果这些代码经q了(jin)适当的测试,那么p很快地发现ƈ解决q个问题?br />     Ҏ(gu)我的l验Q当Z在编写完E序之后才开始编写单元测试,如同事后才有这L(fng)xQ他们很隑ֆ?gu)些测试?jin) ("我已l完成了(jin)所有的代码Q此时我q得d单元试")。或者根本就不去做。在q种情况下,代码是否完成?jin)呢Q如果代码运行地很好Q那q是完成了(jin)。这L(fng)话,再写单元试大大地丧失?jin)它的h(hun)倹{还不仅如此Q事后编写的单元试是肤浅的,不会(x)对代码进行良好地试。或者,开发者已l耗完?jin)时_(d)他们Ҏ(gu)׃惛_为单元测试伤了(jin)?br />     TDD与Q何其它的~码实践一栗当你正在学?fn)某个新的技术时Q你?x)們֐于对学习(fn)指导亦步亦趋。类似地Q当你学?fn)一Ҏ(gu)术时Q你也会(x)试着一步步地模仿大师的动作Q而不必去理解其中的逻辑。一旦你熟?zhn)了(jin)某个技术,能够熟练C用它Qƈ对它有了(jin)更深入地理解Q?em>然后Q你p改进它,q与你之前掌握的其它技术进行溶合了(jin)?br />
译注
[1]John?a >Java Power Tools一书的M者,也是java.net中一位活跃的Blogger?br />
译后
    上周在java.net上看到这BlogQ再联想到自己在qx工作中的单元试实践Q有些感触,故将其翻译了(jin)出来Q与大家׃n?br />     事先q写单元测试,q是事后才编写单元测试?q是一个老问题。按照TDD的思想Q自然是要先~写单元试Q然后再~写能够通过该单元测试的Ҏ(gu)?br />     但,单元试q不是TDD的专属领圎ͼ很多不实践TDD的项目也在应用着单元试?br />     我认为,在不实践TDD的项目中(我自己所处的环境?yu)是如?Q事后编写单元测试仍有着其合理性:(x)
    1. 以消极的态度来看Q既焉目本w不严格要求事先~写单元试Q那么就可以在事后去做了(jin)。这臛_比不d要好Q聊胜于无嘛?嘿嘿Q是够消极的Q但也拿你没办法)
    2. 事后~写单元试臛_也是一U检验手D,当然Q肯定比不上事先~写的单元测试。因为,事后~写的单元测试很可能?就"已经写好的应用程序,正如John所?事后~写的单元测试将是肤的Q不?x)对代码q行良好地测?。但...仍然是聊胜于无嘛 :-D (哈哈Q有完没完了(jin))
    3. 可以把单元测试,其中包含事后单元测试,作ؓ(f)"后来??jin)解、学?fn)应用程序的手段。因为单元测试程序就是应用程序的"客户"Q所以无论它是事先写的,q是事后写的Q都能够表现出应用程序的行ؓ(f)?br />     4. 事后单元试Q也可能转化Z先单元测试。在应用E序的整个生命周期中Q维护阶D|最长的。在"漫长"的维护过E中Q?之前"所写的"事后"单元试会(x)成ؓ(f)"后来?(包括原始作者本??事先"单元试。在改进E序的过E中Q这些单元测试仍然能起到监督的作用?orzQ有点儿诡辩)
    虽然Q事后单元测试明显不如事先单元测试,但它的作用仍然不可低估。只要编写了(jin)优秀的单元测试程序,无论是在哪个阶段Q它都会(x)Ҏ(gu)q应用程序有莫大的帮助?q可不是"聊胜于无"能够表达?



Sha Jiang 2008-06-09 20:55 发表评论
]]>
վ֩ģ壺 ƽ| ̶| | ۷| Ͼ| | ƽ| | ƽ| ӽ| | | | ƽԭ| | | IJ| | Ѱ| | | | | Ƶ| ĩ| Դ| º| | | | | | | | | | ɽ| | | ɽ| |