JUnit4.8.2ソース分析-5.1 Statementの複合コマンド
8414 ワード
抽象クラスStatementはコマンドモードのCommandとして、public abstractvoid evaluate()throws Throwableしかありません.
コマンドモードであるInvokerの様々なRunnerは、様々なStatementを発行し、JUnitテストグループを実行するプロセス全体を表します.方法の表記については、@Test、@Before、@After、@BeforeClass、@AfterClass、各種テストシーンなど、JUnitはorg.junit.internal.runners.statementsパッケージには、Statementのサブクラス、具体的なコマンドが定義されています.
では、一つの方法をテストする場合、@TestのExpectException、FailOnTimeout、RunBefores、RunAftersを処理し、Ruleにも対応する必要があると判断する必要があります.これには、より大きなStatementである複合コマンドが必要です.
ParentRunnerとBlockJUnit 4 ClassRunnerには長いコードがあります.Erich Gammaのスタイルに従って、すべてのタイプが短いのが憎くて、どうして複合コマンドを抽出しないのですか?
BlockJUnit 4 ClassRunnerを例に、orgにかかわらずMethodBlockを設計する.junit.runnersパッケージはツールとしてorgにあります.junit.internal.runners.statementsパッケージはStatementの他のサブクラス(具体的なコマンド)と一緒にいても良いです.関連コードをMethodBlockに配置すると、非常に簡単で明確です.次の約180行のコードはBlockJUnit 4 ClassRunnerから抽出され、みんなは安逸です.
以下のコードでは,それらのアシストメソッドのパラメータは省略できる.
methodBlock(method).evaluate();
new MethodBlock(method,test)に変更する.evaluate();
コマンドモードであるInvokerの様々なRunnerは、様々なStatementを発行し、JUnitテストグループを実行するプロセス全体を表します.方法の表記については、@Test、@Before、@After、@BeforeClass、@AfterClass、各種テストシーンなど、JUnitはorg.junit.internal.runners.statementsパッケージには、Statementのサブクラス、具体的なコマンドが定義されています.
では、一つの方法をテストする場合、@TestのExpectException、FailOnTimeout、RunBefores、RunAftersを処理し、Ruleにも対応する必要があると判断する必要があります.これには、より大きなStatementである複合コマンドが必要です.
ParentRunner
BlockJUnit 4 ClassRunnerを例に、orgにかかわらずMethodBlockを設計する.junit.runnersパッケージはツールとしてorgにあります.junit.internal.runners.statementsパッケージはStatementの他のサブクラス(具体的なコマンド)と一緒にいても良いです.関連コードをMethodBlockに配置すると、非常に簡単で明確です.次の約180行のコードはBlockJUnit 4 ClassRunnerから抽出され、みんなは安逸です.
以下のコードでは,それらのアシストメソッドのパラメータは省略できる.
package org.junit.runners;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test.None;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.internal.runners.statements.ExpectException;
import org.junit.internal.runners.statements.Fail;
import org.junit.internal.runners.statements.FailOnTimeout;
import org.junit.internal.runners.statements.InvokeMethod;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.rules.MethodRule;
/**
* Returns a Statement that, when executed, either returns normally if
* {@code method} passes, or throws an exception if {@code method} fails.
*
* Here is an outline of the default implementation:
*
* <ul>
* <li>Invoke {@code method} on the result of {@code createTest()}, and
* throw any exceptions thrown by either operation.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* expecting} attribute, return normally only if the previous step threw an
* exception of the correct type, and throw an exception otherwise.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* timeout} attribute, throw an exception if the previous step takes more
* than the specified number of milliseconds.
* <li>ALWAYS allow {@code @Rule} fields to modify the execution of the
* above steps. A {@code Rule} may prevent all execution of the above steps,
* or add additional behavior before and after, or modify thrown exceptions.
* For more information, see {@link MethodRule}
* <li>ALWAYS run all non-overridden {@code @Before} methods on this class
* and superclasses before any of the previous steps; if any throws an
* Exception, stop execution and pass the exception on.
* <li>ALWAYS run all non-overridden {@code @After} methods on this class
* and superclasses after any of the previous steps; all After methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
* </ul>
*
* This can be overridden in subclasses, either by overriding this method,
* or the implementations creating each sub-statement.
*/
public class MethodBlock extends Statement{
private Statement statement;
private final FrameworkMethod method;
private final Object test;
public MethodBlock(FrameworkMethod method, Object target) {
this.method= method;
test= target;
}
private void decorator(){
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);
}
/*
* BlockJUnit4ClassRunner
* makeNotifier
*/
//
// Statement builders
//
/**
* Returns a {@link Statement} that invokes {@code method} on {@code test}
*/
protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InvokeMethod(method, test);
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code expecting} attribute, return normally only if {@code next}
* throws an exception of the correct type, and throw an exception
* otherwise.
*
* @deprecated Will be private soon: use Rules instead
*/
@Deprecated
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test, Statement next) {
Test annotation= method.getAnnotation(Test.class);
return expectsException(annotation) ? new ExpectException(next,
getExpectedException(annotation)) : next;
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code timeout} attribute, throw an exception if {@code next}
* takes more than the specified number of milliseconds.
*
* @deprecated Will be private soon: use Rules instead
*/
@Deprecated
protected Statement withPotentialTimeout(FrameworkMethod method,
Object test, Statement next) {
long timeout= getTimeout(method.getAnnotation(Test.class));
return timeout > 0 ? new FailOnTimeout(next, timeout) : next;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @Before}
* methods on this class and superclasses before running {@code next}; if
* any throws an Exception, stop execution and pass the exception on.
*
* @deprecated Will be private soon: use Rules instead
*/
@Deprecated
protected Statement withBefores(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> befores= getTestClass().getAnnotatedMethods(
Before.class);
return befores.isEmpty() ? statement : new RunBefores(statement,
befores, target);
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @After}
* methods on this class and superclasses before running {@code next}; all
* After methods are always executed: exceptions thrown by previous steps
* are combined, if necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
*
* @deprecated Will be private soon: use Rules instead
*/
@Deprecated
protected Statement withAfters(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> afters= getTestClass().getAnnotatedMethods(
After.class);
return afters.isEmpty() ? statement : new RunAfters(statement, afters,
target);
}
private Statement withRules(FrameworkMethod method, Object target,
Statement statement) {
Statement result= statement;
for (MethodRule each : getTestClass().getAnnotatedFieldValues(target,
Rule.class, MethodRule.class))
result= each.apply(result, method, target);
return result;
}
private Class<? extends Throwable> getExpectedException(Test annotation) {
if (annotation == null || annotation.expected() == None.class)
return null;
else
return annotation.expected();
}
private boolean expectsException(Test annotation) {
return getExpectedException(annotation) != null;
}
private long getTimeout(Test annotation) {
if (annotation == null)
return 0;
return annotation.timeout();
}
public final TestClass getTestClass() {
//ParentRunner<T> .validate() ?
return new TestClass(test.getClass());
}
@Override
public void evaluate() throws Throwable {
this.decorator();
statement.evaluate();
}
}
BlockJUnit 4 ClassRunnerではmethodBlock(method).evaluate();
new MethodBlock(method,test)に変更する.evaluate();