<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0
    初次用文字的方式記錄讀源碼的過程,不知道怎么寫,感覺有點貼代碼的嫌疑。不過中間還是加入了一些自己的理解和心得,希望以后能夠慢慢的改進,感興趣的童鞋湊合著看吧,感覺JUnit這個框架還是值得看的,里面有許多不錯的設(shè)計思想在,更何況它是Kent Beck和Erich Gamma這樣的大師寫的。。。。。

    深入JUnit源碼之Rule

    JUnit中的Rule是對@BeforeClass@AfterClass@Before@After等注解的另一種實現(xiàn),其中@ClassRule實現(xiàn)的功能和@BeforeClass@AfterClass類似;@Rule實現(xiàn)的功能和@Before@after類似。JUnit引入@ClassRule@Rule注解的關(guān)鍵是想讓以前在@BeforeClass@AfterClass@Before@After中的邏輯能更加方便的實現(xiàn)重用,因為@BeforeClass@AfterClass@Before@After是將邏輯封裝在一個測試類的方法中的,如果實現(xiàn)重用,需要自己將這些邏輯提取到一個單獨的類中,再在這些方法中調(diào)用,而@ClassRule@Rule則是將邏輯封裝在一個類中,當(dāng)需要使用時,直接賦值即可,對不需要重用的邏輯則可用匿名類實現(xiàn),也因此,JUnit在接下來的版本中更傾向于多用@ClassRule@Rule,雖然就我自己來說,感覺還是用@BeforeClass@AfterClass@Before@After這些注解更加熟悉一些,也可能是我測試代碼寫的還不夠多的原因吧L。同時由于Statement鏈構(gòu)造的特殊性@ClassRule@Rule也保證了類似父類@BeforeClass@Before注解的方法要比子類的注解方法執(zhí)行早,而父類的@AfterClass@After注解的方法執(zhí)行要比子類要早的特點。

    @ClassRule@Rule注解字段的驗證

    @ClassRule@Rule只能注解在字段中,并且該字段的類型必須實現(xiàn)了TestRule接口,對@ClassRule注解的字段還必須是publicstatic,并且@ClassRule注解的字段在運行時不可以拋異常,不然JUnit的行為是未定義的,這個是注釋文檔中這樣描述的,實際情況則一般是直接觸發(fā)testFailure事件,至于其他結(jié)果,則要看不同的TestRule實現(xiàn)不同,這個特征將在下面詳細(xì)講解;而對@Rule注解的字段必須是public,非static,關(guān)于@ClassRule注解字段和@Rule注解字段的驗證是在RuleFieldValidator中做的(具體可以參考Runner小節(jié))

     1 public enum RuleFieldValidator {
     2     CLASS_RULE_VALIDATOR(ClassRule.classtrue), RULE_VALIDATOR(Rule.classfalse);
     3     
     4     public void validate(TestClass target, List<Throwable> errors) {
     5        List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation);
     6        for (FrameworkField each : fields)
     7            validateField(each, errors);
     8     }
     9     private void validateField(FrameworkField field, List<Throwable> errors) {
    10        optionallyValidateStatic(field, errors);
    11        validatePublic(field, errors);
    12        validateTestRuleOrMethodRule(field, errors);
    13     }
    14     private void optionallyValidateStatic(FrameworkField field,
    15            List<Throwable> errors) {
    16        if (fOnlyStaticFields && !field.isStatic())
    17            addError(errors, field, "must be static.");
    18     }
    19     private void validatePublic(FrameworkField field, List<Throwable> errors) {
    20        if (!field.isPublic())
    21            addError(errors, field, "must be public.");
    22     }
    23     private void validateTestRuleOrMethodRule(FrameworkField field,
    24            List<Throwable> errors) {
    25        if (!isMethodRule(field) && !isTestRule(field))
    26            addError(errors, field, "must implement MethodRule or TestRule.");
    27     }
    28     private boolean isTestRule(FrameworkField target) {
    29        return TestRule.class.isAssignableFrom(target.getType());
    30     }
    31     private boolean isMethodRule(FrameworkField target) {
    32        return org.junit.rules.MethodRule.class.isAssignableFrom(target
    33               .getType());
    34     }
    35     private void addError(List<Throwable> errors, FrameworkField field,
    36            String suffix) {
    37        String message= "The @" + fAnnotation.getSimpleName() + " '"
    38               + field.getName() + "" + suffix;
    39        errors.add(new Exception(message));
    40     }
    41 }

     

    JUnit默認(rèn)實現(xiàn)的TestRule

    本節(jié)將重點介紹當(dāng)前JUnit默認(rèn)實現(xiàn)的幾個TestRule,先給出類圖,然后介紹源碼實現(xiàn)以及用途,最后還將簡單的介紹RunRules這個Statement的運行信息,雖然這個類非常簡單,在Statement那節(jié)中也已經(jīng)簡單的做過介紹了。

    在學(xué)一個新的框架的時候,我一直比較喜歡先看一下框架的類圖,這樣自己總體上就有個概念了。這里也先給一張JUnitTestRule的類圖吧:

     

    TestRule的類結(jié)構(gòu)圖還是比較簡單的,只是將它置于JUnitStatement框架中,有些問題分析起來就比較復(fù)雜了。為了保持問題的簡單,我們先來看一下每個單獨的類各自實現(xiàn)了什么功能和怎么實現(xiàn)吧。

    TestWatcherTestName

    先來看兩個簡單的吧,TestWatcher為子類提供了四個事件方法以監(jiān)控測試方法在運行過程中的狀態(tài),一般它可以作為信息記錄使用。如果TestWatcher作為@ClassRule注解字段,則該測試類在運行之前(調(diào)用所有的@BeforeClass注解方法之前)會調(diào)用starting()方法;當(dāng)所有@AfterClass注解方法調(diào)用結(jié)束后,succeeded()方法會被調(diào)用;若@AfterClass注解方法中出現(xiàn)異常,則failed()方法會被調(diào)用;最后,finished()方法會被調(diào)用;所有這些方法的DescriptionRunner對應(yīng)的Description。如果TestWatcher作為@Rule注解字段,則在每個測試方法運行前(所有的@Before注解方法運行前)會調(diào)用starting()方法;當(dāng)所有@After注解方法調(diào)用結(jié)束后,succeeded()方法會被調(diào)用;若@After注解方法中跑出異常,則failed()方法會被調(diào)用;最后,finished()方法會被調(diào)用;所有Description的實例是測試方法的Description實例。

    TestName是對TestWatcher的一個簡單實現(xiàn),它會在starting()方法中記錄每次運行的名字。如果TestName作為@Rule注解字段,則starting()中傳入的Description是對每個測試方法的Description,因而getMethodName()方法返回的是測試方法的名字。一般TestName不作為@ClassRule注解字段,如果真有人這樣用了,則starting()Description的參數(shù)是RunnerDescription實例,一般getMethodName()返回值為null

     1 public abstract class TestWatcher implements TestRule {
     2     public Statement apply(final Statement base, final Description description) {
     3        return new Statement() {
     4            @Override
     5            public void evaluate() throws Throwable {
     6               starting(description);
     7               try {
     8                   base.evaluate();
     9                   succeeded(description);
    10               } catch (AssumptionViolatedException e) {
    11                   throw e;
    12               } catch (Throwable t) {
    13                   failed(t, description);
    14                   throw t;
    15               } finally {
    16                   finished(description);
    17               }
    18            }
    19        };
    20     }
    21     protected void succeeded(Description description) {
    22     }
    23     protected void failed(Throwable e, Description description) {
    24     }
    25     protected void starting(Description description) {
    26     }
    27     protected void finished(Description description) {
    28     }
    29 }
    30 public class TestName extends TestWatcher {
    31     private String fName;
    32     @Override
    33     protected void starting(Description d) {
    34        fName= d.getMethodName();
    35     }
    36     public String getMethodName() {
    37        return fName;
    38     }
    39 }

     

    ExternalResourceTemporaryFolder

    ExternalResource為子類提供了兩個接口,分別是進入測試之前和退出測試之后,一般它是作為對一些資源在測試前后的控制,如Socket的開啟與關(guān)閉、Connection的開始與斷開、臨時文件的創(chuàng)建與刪除等。如果ExternalResource用在@ClassRule注解字段中,before()方法會在所有@BeforeClass注解方法之前調(diào)用;after()方法會在所有@AfterClass注解方法之后調(diào)用,不管在執(zhí)行@AfterClass注解方法時是否拋出異常。如果ExternalResource用在@Rule注解字段中,before()方法會在所有@Before注解方法之前調(diào)用;after()方法會在所有@After注解方法之后調(diào)用。

    TemporaryFolder是對ExternalResource的一個實現(xiàn),它在before()方法中在臨時文件夾中創(chuàng)建一個隨機的文件夾,以junit開頭;并在after()方法將創(chuàng)建的臨時文件夾清空,并刪除該臨時文件夾。另外TemporaryFolder還提供了幾個方法以在新創(chuàng)建的臨時文件夾中創(chuàng)建新的文件、文件夾。

     1 public abstract class ExternalResource implements TestRule {
     2     public Statement apply(Statement base, Description description) {
     3        return statement(base);
     4     }
     5     private Statement statement(final Statement base) {
     6        return new Statement() {
     7            @Override
     8            public void evaluate() throws Throwable {
     9               before();
    10               try {
    11                   base.evaluate();
    12               } finally {
    13                   after();
    14               }
    15            }
    16        };
    17     }
    18     protected void before() throws Throwable {
    19     }
    20     protected void after() {
    21     }
    22 }
    23 public class TemporaryFolder extends ExternalResource {
    24     private File folder;
    25     @Override
    26     protected void before() throws Throwable {
    27        create();
    28     }
    29     @Override
    30     protected void after() {
    31        delete();
    32     }
    33     public void create() throws IOException {
    34        folder= newFolder();
    35     }
    36     public File newFile(String fileName) throws IOException {
    37        File file= new File(getRoot(), fileName);
    38        file.createNewFile();
    39        return file;
    40     }
    41     public File newFile() throws IOException {
    42        return File.createTempFile("junit"null, folder);
    43     }
    44     public File newFolder(String folderNames) {
    45        File file = getRoot();
    46        for (String folderName : folderNames) {
    47            file = new File(file, folderName);
    48            file.mkdir();
    49        }
    50        return file;
    51     }
    52     public File newFolder() throws IOException {
    53        File createdFolder= File.createTempFile("junit""", folder);
    54        createdFolder.delete();
    55        createdFolder.mkdir();
    56        return createdFolder;
    57     }
    58     public File getRoot() {
    59        if (folder == null) {
    60            throw new IllegalStateException("the temporary folder has not yet been created");
    61        }
    62        return folder;
    63     }
    64     public void delete() {
    65        recursiveDelete(folder);
    66     }
    67     private void recursiveDelete(File file) {
    68        File[] files= file.listFiles();
    69        if (files != null)
    70            for (File each : files)
    71               recursiveDelete(each);
    72        file.delete();
    73     }
    74 }

     

    VerifierErrorCollector

    Verifier是在所有測試已經(jīng)結(jié)束的時候,再加入一些額外的邏輯,如果額外的邏輯通過,才表示測試成功,否則,測試依舊失敗,即使在之前的運行中都是成功的。Verify可以為一些很多測試方法加入一些公共的驗證邏輯。當(dāng)Verifier應(yīng)用在@Rule注解字段中,它在所偶@After注解方法運行完后,會調(diào)用verify()方法,如果verifier()方法驗證失敗拋出異常,則該測試方法的testFailure事件將會被觸發(fā),導(dǎo)致該測試方法失敗;當(dāng)Verifier應(yīng)用在@ClassRule時,它在所有的@AfterClass注解的方法執(zhí)行完后,會執(zhí)行verify()方法,如果verify失敗拋出異常,將會觸發(fā)關(guān)于該測試類的testFailure,此時測試類中的所有測試方法都已經(jīng)運行成功了,卻在最后收到一個關(guān)于測試類的testFailure事件,這確實是一個比較詭異的事情,因而@ClassRule中提到ErrorCollectorVerifier)不可以用在@ClassRule注解中,否則其行為為定義;更一般的@ClassRule注解的字段運行時不能拋異常,不然其行為是未定義的。

    ErrorCollector是對Verifier的一個實現(xiàn),它可以在運行測試方法的過程中收集錯誤信息,而這些錯誤信息知道最后調(diào)用ErrorCollectorverify()方法時再處理。其實就目前來看,我很難想象這個需求存在的意義,因為即使它將所有的錯誤信息收集在一起了,在事件發(fā)布是,它還是會為每個錯誤發(fā)布一次testFailure事件(參考EachTestNotifier的實現(xiàn)),除非有一種需求是即使測試方法在運行過程的某個點運行出錯,也只是先記錄這個錯誤,等到所有邏輯運行結(jié)束后才去將這個測試方法運行過程中存在的錯誤發(fā)布出去,這樣一次運行就可以知道測試代碼中存在出錯的地方。ErrorCollector中還提供了幾個收集錯誤的方法:如addError()checkThat()checkSucceeds()等。這里的checkThat()方法用到了hamcrest框架中的Matcher,這部分的內(nèi)容將在Assert小節(jié)中詳細(xì)介紹。

     1 public class Verifier implements TestRule {
     2     public Statement apply(final Statement base, Description description) {
     3        return new Statement() {
     4            @Override
     5            public void evaluate() throws Throwable {
     6               base.evaluate();
     7               verify();
     8            }
     9        };
    10     }
    11     protected void verify() throws Throwable {
    12     }
    13 }
    14 public class ErrorCollector extends Verifier {
    15     private List<Throwable> errors= new ArrayList<Throwable>();
    16     @Override
    17     protected void verify() throws Throwable {
    18        MultipleFailureException.assertEmpty(errors);
    19     }
    20     public void addError(Throwable error) {
    21        errors.add(error);
    22     }
    23     public <T> void checkThat(final T value, final Matcher<T> matcher) {
    24        checkThat("", value, matcher);
    25     }
    26     public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
    27        checkSucceeds(new Callable<Object>() {
    28            public Object call() throws Exception {
    29               assertThat(reason, value, matcher);
    30               return value;
    31            }
    32        });
    33     }
    34     public Object checkSucceeds(Callable<Object> callable) {
    35        try {
    36            return callable.call();
    37        } catch (Throwable e) {
    38            addError(e);
    39            return null;
    40        }
    41     }
    42 }

     

    TimeoutExpectedException

    TimeoutExpectedException都是對@Test注解中timeoutexpected字段的部分替代實現(xiàn)。而且不同于@Test中的注解只適用于單個測試方法,這兩個實現(xiàn)適用于全局測試類。對Timeout來說,如果不是在測試類中所有的測試方法都需要有時間限制,我并不推薦適用Timeout;對ExpectedException它使用了hamcrest中的Matcher來匹配,因而提供了更強大的控制能力,但是一般的使用,感覺@Test中的expected字段就夠了,它多次調(diào)用expected表達是and的關(guān)系,即如果我有兩個Exception,則拋出的Exception必須同時是這兩個類型的,感覺沒有什么大的意義,因而我不怎么推薦使用這個Rule,關(guān)于hamcrestMather框架將在Assert小節(jié)中詳細(xì)介紹。這兩個Rule原本就是基于測試方法設(shè)計的,因而如果應(yīng)用在@ClassRule上好像沒有什么大的意義,不過Timeout感覺是可以應(yīng)用在@ClassRule中的,如果要測試一個測試類整體運行時間的話,當(dāng)然如果存在這種需求的話。

     1 public class Timeout implements TestRule {
     2     private final int fMillis;
     3     public Timeout(int millis) {
     4        fMillis= millis;
     5     }
     6     public Statement apply(Statement base, Description description) {
     7        return new FailOnTimeout(base, fMillis);
     8     }
     9 }
    10 public class ExpectedException implements TestRule {
    11     public static ExpectedException none() {
    12        return new ExpectedException();
    13     }
    14     private Matcher<Object> fMatcher= null;
    15     private ExpectedException() {
    16     }
    17     public Statement apply(Statement base,
    18            org.junit.runner.Description description) {
    19        return new ExpectedExceptionStatement(base);
    20     }
    21     public void expect(Matcher<?> matcher) {
    22        if (fMatcher == null)
    23            fMatcher= (Matcher<Object>) matcher;
    24        else
    25            fMatcher= both(fMatcher).and(matcher);
    26     }
    27     public void expect(Class<? extends Throwable> type) {
    28        expect(instanceOf(type));
    29     }
    30     public void expectMessage(String substring) {
    31        expectMessage(containsString(substring));
    32     }
    33     public void expectMessage(Matcher<String> matcher) {
    34        expect(hasMessage(matcher));
    35     }
    36     private class ExpectedExceptionStatement extends Statement {
    37        private final Statement fNext;
    38        public ExpectedExceptionStatement(Statement base) {
    39            fNext= base;
    40        }
    41        @Override
    42        public void evaluate() throws Throwable {
    43            try {
    44               fNext.evaluate();
    45            } catch (Throwable e) {
    46               if (fMatcher == null)
    47                   throw e;
    48               Assert.assertThat(e, fMatcher);
    49               return;
    50            }
    51            if (fMatcher != null)
    52               throw new AssertionError("Expected test to throw "
    53                      + StringDescription.toString(fMatcher));
    54        }
    55     }
    56     private Matcher<Throwable> hasMessage(final Matcher<String> matcher) {
    57        return new TypeSafeMatcher<Throwable>() {
    58            public void describeTo(Description description) {
    59               description.appendText("exception with message ");
    60               description.appendDescriptionOf(matcher);
    61            }
    62            @Override
    63            public boolean matchesSafely(Throwable item) {
    64               return matcher.matches(item.getMessage());
    65            }
    66        };
    67     }
    68 }

     

    RuleChain

    RuleChain提供一種將多個TestRule串在一起執(zhí)行的機制,它首先從outChain()方法開始創(chuàng)建一個最外層的TestRule創(chuàng)建的Statement,而后調(diào)用round()方法,不斷向內(nèi)層添加TestRule創(chuàng)建的Statement。如其注釋文檔中給出的一個例子:

    1 @Rule
    2 public TestRule chain= RuleChain
    3                     .outerRule(new LoggingRule("outer rule"))
    4                     .around(new LoggingRule("middle rule"))
    5                     .around(new LoggingRule("inner rule"));

     

    如果LoggingRule只是類似ExternalResource中的實現(xiàn),并且在before()方法中打印starting…,在after()方法中打印finished…,那么這條鏈的執(zhí)行結(jié)果為:

    starting outer rule
    starting middle rule
    starting inner rule
    finished inner rule
    finished middle rule
    finished outer rule

     

    由于TestRuleapply()方法是根據(jù)的當(dāng)前傳入的Statement,創(chuàng)建一個新的Statement,以決定當(dāng)前TestRule邏輯的執(zhí)行位置,因而第一個調(diào)用apply()TestRule產(chǎn)生的Statement將在Statement鏈的最里面,也正是有這樣的邏輯,所以around()方法實現(xiàn)的時候,都是把新加入的TestRule放在第一個位置,然后才保持其他已存在的TestRule位置不變。

     1 public class RuleChain implements TestRule {
     2     private static final RuleChain EMPTY_CHAIN= new RuleChain(
     3            Collections.<TestRule> emptyList());
     4     private List<TestRule> rulesStartingWithInnerMost;
     5     public static RuleChain emptyRuleChain() {
     6        return EMPTY_CHAIN;
     7     }
     8     public static RuleChain outerRule(TestRule outerRule) {
     9        return emptyRuleChain().around(outerRule);
    10     }
    11     private RuleChain(List<TestRule> rules) {
    12        this.rulesStartingWithInnerMost= rules;
    13     }
    14     public RuleChain around(TestRule enclosedRule) {
    15        List<TestRule> rulesOfNewChain= new ArrayList<TestRule>();
    16        rulesOfNewChain.add(enclosedRule);
    17        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
    18        return new RuleChain(rulesOfNewChain);
    19     }
    20     public Statement apply(Statement base, Description description) {
    21        for (TestRule each : rulesStartingWithInnerMost)
    22            base= each.apply(base, description);
    23        return base;
    24     }
    25 }

     

    TestRuleStatement的運行

    TestRule實例的運行都是被封裝在一個叫RunRulesStatement中運行的。在構(gòu)造RunRules實例是,傳入TestRule實例的集合,然后遍歷所有的TestRule實例,為每個TestRule實例調(diào)用一遍apply()方法以構(gòu)造出要執(zhí)行TestRuleStatement鏈。類似上小節(jié)的RuleChain,這里在前面的TestRule構(gòu)造的Statement被是最終構(gòu)造出的Statement的最里層,結(jié)合TestClass在獲取注解字段的順序時,先查找子類,再查找父類,因而子類的TestRule實例產(chǎn)生的Statement是在Statement鏈的最里層,從而保證了類似ExternalResource實現(xiàn)中,before()方法的執(zhí)行父類要比子類要早,而after()方法的執(zhí)行子類要比父類要早的特性。

     1 public class RunRules extends Statement {
     2     private final Statement statement;
     3     public RunRules(Statement base, Iterable<TestRule> rules, Description description) {
     4        statement= applyAll(base, rules, description);
     5     }
     6     @Override
     7     public void evaluate() throws Throwable {
     8        statement.evaluate();
     9     }
    10     private static Statement applyAll(Statement result, Iterable<TestRule> rules,
    11            Description description) {
    12        for (TestRule each : rules)
    13            result= each.apply(result, description);
    14        return result;
    15     }
    16 }

     

    posted on 2012-05-12 00:02 DLevin 閱讀(7089) 評論(0)  編輯  收藏 所屬分類: JUnit
    主站蜘蛛池模板: 91嫩草私人成人亚洲影院| 国产一级淫片视频免费看| 亚洲一区二区三区日本久久九| 国产99久久亚洲综合精品| 全部免费毛片在线| 无人视频免费观看免费视频| 四虎永久在线精品免费影视| 色噜噜狠狠色综合免费视频| 免费大黄网站在线观看| 一边摸一边爽一边叫床免费视频| 亚洲国产精品人人做人人爱| 国产精品九九久久免费视频| 亚洲无人区午夜福利码高清完整版 | 最新精品亚洲成a人在线观看| 亚美影视免费在线观看| 亚洲国产另类久久久精品| 久久免费的精品国产V∧| 国产成人无码综合亚洲日韩| GOGOGO免费观看国语| 亚洲国产精品VA在线看黑人| 丁香花在线视频观看免费| 亚洲欧洲日产国码在线观看| 中国在线观看免费国语版| 亚洲色偷偷综合亚洲av78| 日本一线a视频免费观看| 色费女人18女人毛片免费视频| 亚洲精品国自产拍在线观看| 中文字幕免费人成乱码中国| 亚洲国产成人私人影院| 免费精品无码AV片在线观看| 亚洲精品无码久久久久APP| 亚洲成av人片在线观看天堂无码 | 岛国岛国免费V片在线观看| 久久久久亚洲爆乳少妇无| 午夜影院免费观看| 亚洲另类无码专区首页| 亚洲综合色婷婷七月丁香| 亚洲免费视频网址| 乱人伦中文视频在线观看免费| 久久久久亚洲AV无码专区首| 成年女人毛片免费播放人|