﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Jiangshachina-随笔分类-UnitTest</title><link>http://www.blogjava.net/jiangshachina/category/32066.html</link><description>同是Java爱好者，相逢何必曾相识！&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Wed, 04 Jan 2012 13:24:06 GMT</lastBuildDate><pubDate>Wed, 04 Jan 2012 13:24:06 GMT</pubDate><ttl>60</ttl><item><title>探索JUnit4扩展：深入Rule(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 03 Jan 2012 16:13:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/367802.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/367802.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/367802.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong style="font-size: 14pt;"><span style="font-size: 12pt;">探索JUnit4扩展：深入Rule</span><br /></strong></div><span style="font-size: 10pt;">本文是"探索JUnit4扩展"系列中的第三篇，将进一步探究Rule的应用，展示如何使用Rule来替代@BeforeClass，@AfterClass，@Before和@After的功能。(2012.01.04最后更新)</span><br /><br /><span style="font-size: 10pt;">在本系列的第二篇<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">《探索JUnit4扩展：应用Rule》</a>中提到，可以使用Rule替代现有的大部分Runner扩展，而且也不提倡对Runner中的withBefores()，withAfters()等方法进行扩展。本文将介绍如何使用Rule去实现@Before，@After和@BeforeClass的相同功能。</span><br /><br /><strong><span style="font-size: 12pt;">1. BaseRule</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 首先要创建一个较通用的TestRule实现BaseRule，它会释放出两个扩展点，一个在执行测试方法之前，before()；另一个在执行测试方法之后after()。下面是该类的代码，</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">abstract</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;TestRule&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Statement&nbsp;apply(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RuleStatement(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;RuleStatement&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;Statement&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Statement&nbsp;base&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;Description&nbsp;description&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;RuleStatement(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.base&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;base;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.description&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;evaluate()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;before(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base.evaluate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">finally</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;after(base,&nbsp;description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;after(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">如果对JUnit4的源代码略有认知，可能会发现BaseRule与JUnit4提供的TestRule实现ExternalResource代码相似。关键的不同之处是，BaseRule中的before()与after()方法都提供了Statement与Description类型的参数，这使得它能够完成更复杂的工作。</span><br /><br /><strong><span style="font-size: 12pt;">2. CalculatorTest</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文使用的CalculatorTest将不使用@BeforeClass，@Before和@After，而会创建两个BaseRule的实例：一个用于替代@BeforeClass和@AfterClass(本系列目前还未使用过@AfterClass)，另一个则替代@Before和@After。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@ClassRule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;classRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BaseRule()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Rule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;BaseRule&nbsp;rule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BaseRule()&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;before(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBeforeLog(description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;after(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printAfterLog(description);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printBeforeLog(Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;testLogger&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(testLogger&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(testLogger.log());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printAfterLog(Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;end<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero,&nbsp;and&nbsp;an&nbsp;ArithmeticException&nbsp;thrown.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div><span style="font-size: 10pt;">值得注意的是，classRule是静态变量，它使用@ClassRule Annotation，将替代@BeforeClass和@AfterClass；而rule是成员变量，它使用@Rule Annotation，将替代@Before和@After。与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">之前文章</a>不同的是，此处不仅会在执行测试方法之前打印指定内容的日志(printBeforeLog())，还会在执行测试方法之后打印一条固定格式的日志(printAfterLog())，用于指示该测试方法已经执行完毕了。</span><br /><br /><strong><span style="font-size: 12pt;">3. 小结</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 使用Rule可以替代绝大部分的Runner扩展，而且特定的Rule实现可以被复用，也易于添加或移除Rule实例，这些都大大地提高了灵活性。值得注意地是，本文虽然使用Rule代替了@BeforeClass，@AfterClass，@Before和@After的功能，但并不意味着就应当这么做。就我个人所想，将传统的Fixture功能交由@BeforeClass，@AfterClass，@Before和@After实现，仍然是一种不错的选择。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/367802.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2012-01-04 00:13 <a href="http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索JUnit4扩展：应用Rule(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Sat, 24 Dec 2011 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/366801.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/366801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/366801.html</trackback:ping><description><![CDATA[<div><div align="center"><strong><span style="font-size: 14pt;">探索JUnit4扩展：使用Rule</span></strong></div>在上一篇文章<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">《探索JUnit4扩展：扩展Runner》</a>中，讨论了一种扩展JUnit4的方式，即，直接修改Test Runner的实现(BlockJUnit4ClassRunner)。但这种方法显然不便于灵活地添加或删除扩展功能。本文将使用JUnit4.7才开始引入的扩展方式--Rule来实现相同的扩展功能。(2010.12.25最后更新)<br /><br /><strong style="font-size: 12pt;">1. Rule</strong><br style="font-size: 12pt;" />Rule是JUnit4.7才开始提供的一种扩展方式，它能够替代大部分已有的Runner扩展。JUnit包含两种Rule Annotation：@ClassRule与@Rule。@ClassRule应用于测试类中的静态变量，而@Rule应用于成员变量；相同地是，这些变量必须是TestRule接口的实例，且访问修饰符必须为public。<br />在<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中，对BlockJUnit4ClassRunner进行了扩展，被扩展的方法是methodBlock，现在我们来看看该方法体中的代码，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Statement&nbsp;methodBlock(FrameworkMethod&nbsp;method)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;test;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ReflectiveCallable()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Object&nbsp;runReflectiveCall()&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Throwable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;createTest();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}.run();<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Throwable&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Fail(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;methodInvoker(method,&nbsp;test);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;possiblyExpectingExceptions(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withPotentialTimeout(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withBefores(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withAfters(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;statement</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;withRules(method,&nbsp;test,&nbsp;statement);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;statement;<br />}</span></div>但在BlockJUnit4ClassRunner中，possiblyExpectingExceptions()，withPotentialTimeout()，withBefores()和withAfters()都已经被标注为过时，JUnit建议使用Rule来替代这些方法的功能。<br /><br /><strong style="font-size: 12pt;">2. TestLogRule</strong><br style="font-size: 12pt;" />如第1节所述，Rule Annotation要作用于TestRule接口的实例，那么就要先创建一个TestRule的实现类。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;TestRule&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Statement&nbsp;apply(Statement&nbsp;base,&nbsp;Description&nbsp;description)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;testLogger&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;description.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(testLogger&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(description.getClassName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(description.getMethodName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(testLogger.log());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;base;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>如上所示，TestLogRule与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中的LoggedRunner的代码有许多相同之处，功能则都是打印出指定的日志，每行日志又以当时的执行时间与完整方法名作为前缀。<br /><br /><strong><span style="font-size: 12pt;">3. 使用Rule的CalculatorTest</span></strong><br />下面是新的测试类CalculatorTest，它将不使用BlockJUnit4ClassRunner的扩展LoggedRunner作为测试执行器，所以该类没有使用@RunWith(LoggedRunner.class)，那么在执行该测试类时仍然会使用BlockJUnit4ClassRunner。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Rule<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;testLogRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;TestLogRule();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@BeforeClass<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;createCalculator()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero,&nbsp;and&nbsp;an&nbsp;ArithmeticException&nbsp;thrown.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>与<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html">上篇博文</a>中的CalculatorTest相比，本文中的CalculatorTest除了没有使用LoggedRunner之外，还多了两行代码<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">@Rule<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;TestLogRule&nbsp;testLogRule&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;TestLogRule();</span></div>在执行单元测试方法之前，BlockJUnit4ClassRunner会调用TestRule/TestLogRule中的apply()方法，即，会先打印出日志内容。<br /><br /><strong style="font-size: 12pt;">4. 小结</strong><br style="font-size: 12pt;" />使用Rule对JUnit进行扩展，能够避免对默认Runner的扩展，为测试类添加或移除Rule十分方便，而且Rule实现类本身也能很方便地被复用。在<a href="http://www.blogjava.net/jiangshachina/archive/2012/01/04/367802.html">下一篇博文</a>中将进一步探索Rule的应用。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/366801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-12-24 23:26 <a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索JUnit4扩展：扩展Runner(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 13 Dec 2011 16:01:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/366289.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/366289.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/366289.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong style="font-size: 14pt;">探索JUnit4扩展：扩展Runner<br />
</strong></div>
在使用JUnit的过程中，大家可能会对JUnit进行一些扩展。本文中的示例为JUnit4定义了一个新的Annotation，并相应地对已有的Runner进行扩展，使其能够解析新引入的Annotation。(2011.12.25最后更新)<br />
<br />
本文臆造了一个示例，会在执行单元测试方法之前，自动地为单元测试方法打印日志。该示例会为JUnit定义一个新的Annotation用于指定要打印的日志内容，并对JUnit默认提供的Runner实现BlockJUnit4ClassRunner进行扩展，使其能够识别这个新的Annotation。<br />
<br />
<strong style="font-size: 12pt;">1. 定义Annotation</strong><br style="font-size: 12pt;" />
&nbsp;&nbsp;&nbsp; TestLogger是一个作用于方法的Annotation，它只有一个属性，用于指定日志的内容，其代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">@Target({&nbsp;ElementType.METHOD&nbsp;})<br />
@Retention(RetentionPolicy.RUNTIME)<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;@</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;TestLogger&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;String&nbsp;log()&nbsp;</span><span style="color: #0000FF; ">default</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">""</span><span style="color: #000000; ">;<br />
}</span></div>
<br />
<strong><span style="font-size: 12pt;">2. 扩展Runner</span></strong><br />
&nbsp;&nbsp;&nbsp; JUnit提供了若干个Runner的实现，如BlockJUnit4ClassRunner，Suite，其中BlockJUnit4ClassRunner用来执行单个测试用例类。LoggedRunner将扩展BlockJUnit4ClassRunner，覆写其中的methodBlock()方法。新的methodBlock()方法会在一开始试图获取被执行测试方法中的TestLogger Annotation，如果存在的话，就会打印出指定的日志，每行日志以当时的执行时间与完整方法名作为前缀。该类的代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;LoggedRunner&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;BlockJUnit4ClassRunner&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;DateFormat&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;SimpleDateFormat(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">yyyy-MM-dd_HH:mm:ss_SSS</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;LoggedRunner(Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;klass)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;InitializationError&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">(klass);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">protected</span><span style="color: #000000; ">&nbsp;Statement&nbsp;methodBlock(FrameworkMethod&nbsp;method)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;classMethod&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;method.getMethod();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestLogger&nbsp;loggerAnnotation&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;classMethod.getAnnotation(TestLogger.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(loggerAnnotation&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;StringBuilder(format.format(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Date()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(classMethod.getDeclaringClass().getName())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">#</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).append(classMethod.getName()).append(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.append(loggerAnnotation.log());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(log.toString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">.methodBlock(method);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<strong style="font-size: 12pt;">3. 应用程序</strong><br style="font-size: 12pt;" />
&nbsp;&nbsp;&nbsp; Calculator是一个简单的应用程序，其中定义了一个除法方法，代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;divide(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;a,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;b)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;a&nbsp;</span><span style="color: #000000; ">/</span><span style="color: #000000; ">&nbsp;b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<strong><span style="font-size: 12pt;">4. 单元测试程序</span></strong><br />
&nbsp;&nbsp;&nbsp; CalculatorTest是一个简单的单元测试程序，它会使用两种方式对Calculator中的divide()方法进行单元测试。其代码如下所示，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">@RunWith(LoggedRunner.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CalculatorTest&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calculator&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@BeforeClass<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;createCalculator()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Calculator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test<br />
&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a&nbsp;simple&nbsp;division</span><span style="color: #000000; ">."</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;simpleDivide()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.assertTrue(value&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Test(expected&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;ArithmeticException.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;@TestLogger(log&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">divided&nbsp;by&nbsp;zero</span><span style="color: #000000; ">, and an ArithmeticException thrown."</span><span style="color: #000000; ">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;dividedByZero()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calculator.divide(</span><span style="color: #000000; ">8</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
值得注意的是，CalculatorTest特别指定LoggedRunner作为测试执行器(@RunWith(LoggedRunner.class))；同时，每个单元测试方法，simpleDivide()与dividedByZero()，都使用了Annotation TestLogger，为其指定日志内容。当执行上述单元测试时，会自动地打印出如下形式的日志内容：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">2011</span><span style="color: #000000; ">-</span><span style="color: #000000; ">12</span><span style="color: #000000; ">-13_</span><span style="color: #000000; ">23</span><span style="color: #000000; ">:</span><span style="color: #000000; ">48</span><span style="color: #000000; ">:38_</span><span style="color: #000000; ">218</span><span style="color: #000000; "> test.CalculatorTest#simpleDivide:&nbsp;a&nbsp;simple&nbsp;division<br />
</span><span style="color: #000000; ">2011</span><span style="color: #000000; ">-</span><span style="color: #000000; ">12</span><span style="color: #000000; ">-13_</span><span style="color: #000000; ">23</span><span style="color: #000000; ">:</span><span style="color: #000000; ">48</span><span style="color: #000000; ">:38_</span><span style="color: #000000; ">218</span><span style="color: #000000; "> test.CalculatorTest#dividedByZero:&nbsp;divided&nbsp;by&nbsp;zero, and an ArithmeticException thrown.<br />
</span></div>
</div><br /><strong style="font-size: 12pt;">5. 小结</strong><br style="font-size: 12pt;" />通过对BlockJUnit4ClassRunner的扩展，可以让JUnit在运行测试用例时做一些额外的工作。但这种直接修改默认Test Runner的方式并不被提倡，在<a href="http://www.blogjava.net/jiangshachina/archive/2011/12/24/366801.html">下一篇文章</a>中将会介绍使用Test Rule来达到相同的扩展目的。<br />&nbsp;<img src ="http://www.blogjava.net/jiangshachina/aggbug/366289.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2011-12-14 00:01 <a href="http://www.blogjava.net/jiangshachina/archive/2011/12/14/366289.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写好的面向对象代码(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Tue, 07 Oct 2008 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/232942.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/232942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/232942.html</trackback:ping><description><![CDATA[<div align="center"><span style="font-size: 10pt;"><strong style="font-size: 14pt;">编写好的面向对象代码</strong></span><br />
</div>
<p align="left"><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文是<a href="http://www.java.net">java.net</a>上的一篇<a href="http://weblogs.java.net/blog/thedarksavant/archive/2008/10/writing_great_o_1.html">博客</a>，作者<a href="http://weblogs.java.net/blog/thedarksavant/">Curtis Cooley</a>对编写好的面向对象代码有些建议，希望对大家都有所帮助。(2008.10.08最后更新)<br />
</span><span style="font-size: 10pt;"><br />
获取经验没有捷径。编写好的面向对象代码需要经验，但这儿有三种做法能帮你在一开始就很顺利，即便你是老顽固：<br />
&nbsp;&nbsp;&nbsp; 1. 使用测试驱动开发(TDD)编写你所有的代码<br />
&nbsp;&nbsp;&nbsp; 2. 遵循<a href="http://xp.c2.com/XpSimplicityRules.html">简单法则</a><br />
&nbsp;&nbsp;&nbsp; 3. 告之而非问之<br />
<br />
</span><span style="font-size: 10pt;"><strong style="font-size: 12pt;">使用TDD编写所有代码</strong><br />
&nbsp;&nbsp;&nbsp; 按<a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/tdd-is-design-activity.html">测试先行</a>编写的代码与按测试后行编写的代码是极为不同的代码。按测试先行编写的代码是松耦合与高聚合的。当某个属性或私有方法需要暴露给测试程序时，按测试后行编写的代码常会打破封装，因为该类并不是为了测试而设计的。如果你首先编写测试代码，你的依赖将会更好，你的代码将是松耦合与高聚合的。后面会有更多关于测试能帮助你设计更佳代码的内容。<br />
<br />
<strong style="font-size: 12pt;">遵循简单法则</strong><br />
&nbsp;&nbsp;&nbsp; 代码是简单的，只要当它：<br />
&nbsp;&nbsp;&nbsp; 1. 执行了所有的测试<br />
&nbsp;&nbsp;&nbsp; 2. 不包含重复<br />
&nbsp;&nbsp;&nbsp; 3. 表达了所有的意图<br />
&nbsp;&nbsp;&nbsp; 4. 使用最少的类和方法<br />
注意到我用的是个被排序了的列表是很重要的。顺序是重要的。只有一个main()方法的的GodClass<sup>[1]</sup>不会是简单的。这个类可能执行了所有的测试，但在任何比"Hello, world!"更复杂的程序中，它肯定包含了重复，并且也没有表达出全部的意图。<br />
我努力使用简单法则去关注<a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html">If问题</a>。我不知道如何使用简单法则去阻止某人编写重量级的If代码。有人可能会提出不同意见，我也尝试过，但这样的重量级If代码确实无法表达意图。但当你阅读如下代码时</span></p>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.STANDARD)&nbsp;{<br />
&nbsp;&nbsp;alert();<br />
}</span></div>
<span style="font-size: 10pt;">确实难以看出其中的意图。这些代码无论处于哪个方法的上下文环境中，我们都能知道，如果mobile是STANDARD类型的话，那么就报警。而你所需要的更多意图呢？<br />
我还有一点儿灵感显现。如果有那样的代码，那么在其它地方肯定还会有更多那样的代码。这些代码可能就像：<br />
</span>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.GAS)&nbsp;{<br />
&nbsp;&nbsp;registerGasReading();<br />
}</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.TEXT)&nbsp;{<br />
&nbsp;&nbsp;sendTextMessage();<br />
}</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType()&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.LOCATION)&nbsp;{<br />
&nbsp;&nbsp;notifyLocation();<br />
}</span></div>
<span style="font-size: 10pt;">你看出来了吗？我是看出来了。它违反了规则2，有很多地方都违反了规则2，并且是一种最坏的情形。这段代码有多处重复。重复将极难发现。所以，请帮助防止这种情形的发生，我已包含其中了。<br />
<br />
<strong style="font-size: 12pt;">告之而非问之</strong><br />
简言之，<a href="http://www.pragmaticprogrammer.com/articles/tell-dont-ask">告之而非问之</a>意指不要先问一个对象的状态，然后才让它去工作。而应该告之对象如何去工作。这就意味着之前所有的那些If例子应该变为：</span> <br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.alert();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 10pt; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.registerGasReading();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.sendTextMessage();</span></div>
<span style="font-size: 10pt;">和</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">mobile.notifyLocation();</span></div>
<span style="font-size: 10pt;">现假设遍布该程序中的一些If语句块有重复的实现。在"重量级If"版本的程序中，可能很难发现它们；但在"告之而非问之"版本的程序中，所有的实现都在Mobile中。所有的实现都在一处，这就便于察觉并根除问题。<br />
&nbsp;&nbsp;&nbsp; 倾听你的测试程序也能帮助你保持代码的简洁。</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">interface</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert(Mobile&nbsp;mobile);<br />
}<br />
<br />
</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;Siren&nbsp;</span><span style="color: rgb(0, 0, 255);">implements</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert(Mobile&nbsp;mobile)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(mobile.getType&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;MobileTypes.STANDARD)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soundSiren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
}<br />
<br />
</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;TestSiren&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;TestCase&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;test_alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;LocationMobile&nbsp;mobile&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Siren&nbsp;siren&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;Siren();<br />
&nbsp;&nbsp;&nbsp;&nbsp;siren.alert(mobile);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">assert</span><span style="color: rgb(0, 0, 0);">(sirenSounded());<br />
&nbsp;&nbsp;}<br />
}</span></div>
<span style="font-size: 10pt;">如果你密切地倾听测试程序，它可能会问你，"为什么你需要一个LocationMobile去测试Siren呢？"的确，为什么呢？看起来，Siren应该还不知道LocationMobile吧。</span><br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);">&nbsp;Alarm&nbsp;alarm;<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile(Alarm&nbsp;alarm)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.alarm&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;alarm;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;alarm.alert();&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;alert&nbsp;on&nbsp;Alarm&nbsp;no&nbsp;longer&nbsp;needs&nbsp;a&nbsp;mobile</span><span style="color: rgb(0, 128, 0);"><br />
</span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;}<br />
}<br />
<br />
</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;TestLocationMobile()&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;TestCase&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;test_alert()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Alarm&nbsp;alarm&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;EasyMock.createMock(Alarm.</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;alarm.alert();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EasyMock.replay(alarm);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Mobile&nbsp;mobile&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;LocationMobile(alarm);<br />
&nbsp;&nbsp;&nbsp;&nbsp;mobile.alert();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EasyMock.verify(alarm);<br />
}</span></div>
<span style="font-size: 10pt;">好像我只是交换了依赖关系。Alarm不再依赖Mobile，现在是Mobile依赖Alarm。但如果你仔细地观察这个测试程序，你会发现真正的依赖关系是，Siren知晓了LocationMobile。一个具体类依赖另一个具体类，这违反了<a href="http://c2.com/cgi/wiki?DependencyInversionPrinciple">依赖反转原则</a>(DIP)。第二个例子就让LocationMobile依赖Alarm接口。具体类依赖抽象，这就满足DIP了。<br />
&nbsp;&nbsp;&nbsp; 如果你使用TDD，并遵循简单法则和告之而非问之原则去编写所有的代码，你就处于成为一个更好的面向对象程序员的道路上了。好的面向对象代码易于阅读和维护，但难以编写，至少，在开始时是这样的。你写的越多，你就会变得越好，也会获得更多的经验。同时，这些实践经验也会使你在自己的道路上受益匪浅。<br />
<br />
<strong>译注</strong><br />
[1]GodClass(上帝类)指包含了太多内容的类。<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/232942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2008-10-07 17:06 <a href="http://www.blogjava.net/jiangshachina/archive/2008/10/07/232942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>何时编写单元测试？(译)</title><link>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html</link><dc:creator>Sha Jiang</dc:creator><author>Sha Jiang</author><pubDate>Mon, 09 Jun 2008 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/206812.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/206812.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/206812.html</trackback:ping><description><![CDATA[<div align="center"><strong><span style="font-size: 10pt;"><span style="font-size: 14pt;">何时编写单元测试？</span></span></strong><br />
</div>
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 是在编写一个方法之前就编写它的单元测试，还是在写完这个方法，甚至是整个类之后才编写单元测试呢？John Ferguson Smart<sup>[1]</sup>在他的<a href="http://weblogs.java.net/blog/johnsmart/archive/2008/06/tests_first_or.html">blog</a>中再次提出了这个问题，并根据自己的经验给出了一些建议。(2008.06.10最后更新)<br />
<br />
&nbsp;&nbsp;&nbsp; 都别书生意气了。在你编写一个方法之前或是之后编写单元测试--根据我的经验，只要你在编写代码的<em>几乎同时</em>就考虑并编写单元测试程序，那么这就无关紧要了。过后再返回去(或者根本就不回去)写测试程序将导致问题。就我个人而言，我喜欢在编写少量代码之前或紧接着的之后就编写单元测试--这不会打破工作流程，因为<em>它就是流程的一部分</em>。<br />
&nbsp;&nbsp;&nbsp; 这需要一点儿实践经验--缺乏经验的开发者经常为要写什么样的测试程序而烦恼。但这可能也反映出一个事实：他们同样也不知道要写什么样的代码。一些人评说TDD能够鼓励进行微设计--一种非常底层的设计，它不需要考虑较大的场景。这会发生在缺乏经验的开发者身上；如果你教条般地应用这种方法，同样也会遇上。像行为驱动开发这样的方法在此处就会很酷。当你在写getter方法之前，你会写一个针对这个getter方法的单元测试吗？如果是的话，那么你的单元测试专注的层次就较高了，也会更接近于用户(或系统)的需求。<br />
&nbsp;&nbsp;&nbsp; 回到问题的本质，为什么我喜欢把单元测试放在最开始的位置？很简单！我的实践经验告诉我，那样可以帮助提高代码的质量，并且节约调试时间。在开始时写十个小的单元测试所花的时间比在以后修复Bug所花的时间要少，如果代码经过了正确的单元测试，那就不会有Bug了。<br />
&nbsp;&nbsp;&nbsp; 事实上，我屡屡见到，如果某些代码经过了适当的单元测试，那么就不会有编码问题。最近就有一个例子：花了一个小时的时间去搜寻Web应用中的一个问题，该问题出现在一个编写正确的Spring-MVC程序中。结果是由于一个检验器类忽略了一个异常。很容易就发现了这个问题，实际上，在看了代码(代码检查(Code Review)也很有效)之后立刻就发现了。但关键是，我们花了一个小时甚至更多的时间去找这个需要进行检查的类。如果这些代码经过了适当的测试，那么就能很快地发现并解决这个问题。<br />
&nbsp;&nbsp;&nbsp; 根据我的经验，当人们在编写完程序之后才开始编写单元测试，就如同事后才有这样的想法，他们很难写出这些测试了 ("我已经完成了所有的代码，此时我还得去写单元测试")。或者根本就不去做。在这种情况下，代码是否完成了呢？如果代码运行地很好，那就算是完成了。这样的话，再写单元测试就大大地丧失了它的价值。还不仅如此，事后编写的单元测试将是肤浅的，不会对代码进行良好地测试。或者，开发者已经耗完了时间，他们根本就不想再为单元测试伤神了。<br />
&nbsp;&nbsp;&nbsp; TDD与任何其它的编码实践一样。当你正在学习某个新的技术时，你会倾向于对学习指导亦步亦趋。类似地，当你学习一项武术时，你也会试着一步步地模仿大师的动作，而不必去理解其中的逻辑。一旦你熟悉了某个技术，能够熟练地使用它，并对它有了更深入地理解，<em>然后</em>，你就能改进它，并与你之前掌握的其它技术进行溶合了。<br />
<br />
<strong><span style="font-size: 12pt;">
译注</span></strong><br />
[1]John是<a href="http://www.javapowertools.com/">Java Power Tools</a>一书的主作者，也是<a href="http://www.java.net/">java.net</a>中一位活跃的<a href="http://weblogs.java.net/blog/johnsmart/">Blogger</a>。<br />
<br />
<strong><span style="font-size: 12pt;">译后</span></strong><br />
&nbsp;&nbsp;&nbsp; 上周在java.net上看到这篇Blog，再联想到自己在平时工作中的单元测试实践，有些感触，故将其翻译了出来，与大家共享。<br />
&nbsp;&nbsp;&nbsp; 事先就编写单元测试，还是事后才编写单元测试？这是一个老问题。按照TDD的思想，自然是要先编写单元测试，然后再编写能够通过该单元测试的方法。<br />
&nbsp;&nbsp;&nbsp; 但，单元测试并不是TDD的专属领地，很多不实践TDD的项目也在应用着单元测试。<br />
&nbsp;&nbsp;&nbsp; 我认为，在不实践TDD的项目中(我自己所处的环境就是如此)，事后编写单元测试仍有着其合理性：<br />
&nbsp;&nbsp;&nbsp; 1. 以消极的态度来看，既然项目本身不严格要求事先编写单元测试，那么就可以在事后去做了。这至少比不去做要好，聊胜于无嘛。(嘿嘿，是够消极的，但也拿你没办法)<br />
&nbsp;&nbsp;&nbsp; 2. 事后编写单元测试至少也是一种检验手段，当然，肯定比不上事先编写的单元测试。因为，事后编写的单元测试很可能会"将就"已经写好的应用程序，正如John所说"事后编写的单元测试将是肤浅的，不会对代码进行良好地测试"。但...仍然是聊胜于无嘛 :-D (哈哈，有完没完了)<br />
&nbsp;&nbsp;&nbsp; 3. 可以把单元测试，其中就包含事后单元测试，作为"后来者"了解、学习应用程序的手段。因为单元测试程序就是应用程序的"客户"，所以无论它是事先写的，还是事后写的，都能够表现出应用程序的行为。<br />
&nbsp;&nbsp;&nbsp; 4. 事后单元测试，也可能转化为事先单元测试。在应用程序的整个生命周期中，维护阶段是最长的。在"漫长"的维护过程中，"之前"所写的"事后"单元测试将会成为"后来者"(包括原始作者本人)的"事先"单元测试。在改进程序的过程中，这些单元测试仍然能起到监督的作用。(orz，有点儿诡辩)<br />
&nbsp;&nbsp;&nbsp; 虽然，事后单元测试明显不如事先单元测试，但它的作用仍然不可低估。只要编写了优秀的单元测试程序，无论是在哪个阶段，它都会对改进应用程序有莫大的帮助。(这可不是"聊胜于无"能够表达的)<br />
<br />
</span>
<img src ="http://www.blogjava.net/jiangshachina/aggbug/206812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">Sha Jiang</a> 2008-06-09 20:55 <a href="http://www.blogjava.net/jiangshachina/archive/2008/06/09/206812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>