`
chris_freedream
  • 浏览: 32944 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

JUnit 中一笔优雅的设计

阅读更多
...
setUp();
testXXX();
tearDown();
用过JUnit的对这些应该是烂熟于胸了,这三部曲简述了JUnit的最基本的操作过程,JUnit的设计者为了保证这三部操作能像原子操作般执行,使用了一个很通用的设计模式,Template pattern, 典型的模板模式基本上是按照下面的方式来设计的:

/**
*定义一个抽象的类,并定义几个要步骤化的抽象方法,
×把这些操作交付给起子孙后代去实现,
*再定义一个总控这个步骤的方法--模板方法,这里对manipulate做了final修饰,
×不让其子类改写,当然如果开明点,也可以把这个final给去掉,这样其子孙也
×也有权利设计这个操作内容了。
*/
public abstract class A{
  protected abstract void operOne();
  protected abstract void operTwo();
  protected abstract void operThree();
  public final void manipulate(){
     operOne();
     operTwo();
     operThree();
  }
}

/**
*继承A,并实现自己的操作
*/
public class SubA{

  protected void operOne(){
      System.out.pritln("step one");
  }
  protected void operTwo(){
      System.out.pritln("step two");
  }
  protected void operThree(){
      System.out.pritln("step three");
  }
}

JUnit是这样做的吗?肯定不是了。那是怎样处理的呢?
让我们来赏析一下JUnit的内核源码片段:
package junit.framework;

import java.util.Vector;
import java.util.Enumeration;

/**
 * A <code>TestResult</code> collects the results of executing
 * a test case. It is an instance of the Collecting Parameter pattern.
 * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
 * A failure is anticipated and checked for with assertions. Errors are
 * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
 *
 * @see Test
 */
public class TestResult extends Object {
...
	/**
	 * Runs a TestCase.
	 */
	public void runProtected(final Test test, Protectable p) {
		try {
			p.protect();
		} 
		catch (AssertionFailedError e) {
			addFailure(test, e);
		}
		catch (ThreadDeath e) { // don't catch ThreadDeath by accident
			throw e;
		}
		catch (Throwable e) {
			addError(test, e);
		}
	}
	/**
	 * Runs a TestCase.
	 */
	protected void run(final TestCase test) {
		startTest(test);
		Protectable p= new Protectable() {
			public void protect() throws Throwable {
				test.runBare();
			}
		};
		runProtected(test, p);

		endTest(test);
	}
...
}

以上是TestResult摘录的一个片段,这里要强调的是接口Protectable,这个接口就是用来保护setUp(),runBase(),tearDown()操作的,也就是说在执行某个测试的时候系统一定要经过这些步骤,当然JUnit的作者是在哪些地方去实现了Protectabe接口的呢?笔者去跟踪了源码,实现接口的位置有两处,如下:
public abstract class TestCase extends Assert implements Test {
...
	/**
	 * Runs the bare test sequence.
	 * @exception Throwable if any exception is thrown
	 */
	public void runBare() throws Throwable {
		setUp();
		try {
			runTest();
		}
		finally {
			tearDown();
		}
	}
	public TestResult run() {
		TestResult result= createResult();
		run(result);
		return result;
	}
...
}

另外一处:
package junit.extensions;

import junit.framework.*;

/**
 * A Decorator to set up and tear down additional fixture state.
 * Subclass TestSetup and insert it into your tests when you want
 * to set up additional state once before the tests are run.
 */
public class TestSetup extends TestDecorator {

	public TestSetup(Test test) {
		super(test);
	}
	public void run(final TestResult result) {
		Protectable p= new Protectable() {
			public void protect() throws Exception {
				setUp();
				basicRun(result);
				tearDown();
			}
		};
		result.runProtected(this, p);
	}
	/**
	 * Sets up the fixture. Override to set up additional fixture
	 * state.
	 */
	protected void setUp() throws Exception {
	}
	/**
	 * Tears down the fixture. Override to tear down the additional
	 * fixture state.
	 */
	protected void tearDown() throws Exception {
	}
}

到这想必大家已经基本上理解了设计者的意图了,但相比较于继承父类的方式来实现模板化,使用接口更加体现了系统的灵活性,假设前面提到的类A是由第三方提供的component,但这个component却没有提供类似的模板方面,这个时候我们可以考虑使用模板化接口来解决这个问题,但是作者可不是直接实现接口,而是采用方法中调用Protectable的方式来事处理了,这更加体现了系统的灵活度(当然这也是Inversion Of Contorl理念之一)。
3
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics