使用过spring,然后我们都知道如何使用spring的junit扩展来进行测试,所以我觉得很有必要研究下自定义运行器。比如下面的代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/spring-mvc-servlet.xml", "/applicationContext-db.xml" })
@WebAppConfiguration
public class FlowServletTest extends BaseTest
{
@Autowired
private WebApplicationContext wac;
private MockMvc mock;
@Before
public void init()
{
this.mock = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
}
前面的博客我已经整理到了,junit的核心由3个类组成,TestClass,Runner,Suite,注意的是这个suite本身就是一个runner。
认真看过前面的博客,我们就会明白这些类的工作原理,然后我们可以使用junit编写任何需要的测试。
这里我们来自定义运行器来扩展junit。OK,废话不多说了,现在我们开始。
拦截器模式可以看做一种拦截业务方法调用的方法。这种模式通常包含了几个对象,第一个对象就是Interceptor接口,它定义了你的拦截器将实现的一个或多个方法。
第二个对象是Delegate(委托)对象,它持有一系列拦截器。Delegate对象在应用程序的拦截点被调用,并且它会一个接着一个的调用拦截器。
总结一下使用拦截器模式的功能:
1,它带来了可扩展性和灵活性
2,它是的关注分离
3,它增加了可重用性
4,如果没有被正确使用,他可能出现安全问题。
考虑如下一个情节,你正在为一组开发人员设计一个程序。你想让他们尽可能容易的将他们的代码植入你的应用程序中,同时,你又不希望他们改变他。那我们应该要怎么做呢?
你可以在你的应用程序中提供一些点,让那些开发人员能够拦截程序的调用并引入他们自己的逻辑。而其他人无须改变你的代码,他们可以简单的将他们的代码植入你的框架。
OK,现在让我们开始实现自定义运行器。
1,首先写一个拦截器接口,这个拦截器可以通过我们已有的各种拦截器来实现。
package org.linkinpark.junit.testjunit;
/**
* @创建作者: LinkinPark
* @创建时间: 2016年2月14日
* @功能描述: 自定义拦截器接口。
*/
public interface Interceptor
{
/**
* @创建时间: 2016年2月14日
* @相关参数:
* @功能描述: 过滤器绕前拦截
*/
public void interceptBefore();
/**
* @创建时间: 2016年2月14日
* @相关参数:
* @功能描述: 过滤器绕后拦截
*/
public void interceptAfter();
}
关于上面的代码,我们自定义2个接口,然后绕前和绕后插入我们自己定义的拦截内容。注意,这里我们的拦截器不接受任何的参数。实现拦截器最常见的模式就是以上下文对象调用拦截器方法,从而能够使得这些方法可以监控和访问我们的应用程序。这样子就可以让我们的应用程序从我们自定义的拦截器方法中获得一些反馈。就目前我们先简单的写2个无参方法好了。
2,现在我们来写delegate对象。前面的博客我也已经整理到了,junit真正的测试执行是在各种Statement抽象类的实现中。那OK,我们现在就写一个自己的测试块。
package org.linkinpark.junit.testjunit;
import java.util.ArrayList;
import java.util.List;
import org.junit.runners.model.Statement;
public class InterceptorStatement extends Statement
{
private final Statement invoker;
private List<Interceptor> interceptors = new ArrayList<>(2);
public InterceptorStatement(Statement invoker)
{
this.invoker = invoker;
}
@Override
public void evaluate() throws Throwable
{
for (Interceptor interceptor : interceptors)
{
interceptor.interceptBefore();
}
invoker.evaluate();
for (Interceptor interceptor : interceptors)
{
interceptor.interceptAfter();
}
}
public void addInterceptor(Interceptor interceptor)
{
interceptors.add(interceptor);
}
}
关于上面的代码,我的意图就是将原来junit中的测试作为invoker参数封装到我们自定义的InterceptorStatement中,接下来然后将我们自定义的这个语句传递给我们自己写的测试运行器。
3,现在让我们来写自定义的拦截器。
package org.linkinpark.junit.testjunit;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
public class InterceptorRunner extends BlockJUnit4ClassRunner
{
public InterceptorRunner(Class<?> klass) throws InitializationError
{
super(klass);
}
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test)
{
InterceptorStatement statement = new InterceptorStatement(super.methodInvoker(method, test));
InterceptorClasses annotation = test.getClass().getAnnotation(InterceptorClasses.class);
Class<?>[] klass = annotation.values();
try
{
for (Class<?> klas : klass)
{
statement.addInterceptor((Interceptor)klas.newInstance());
}
}
catch (Exception e)
{
e.printStackTrace();
}
return statement;
}
}
关于上面的代码,我们自定义一个测试运行器,该类继承BlockJUnit4ClassRunner类。上面的代码我们使用到了一个注解,这个注解来配合我们将我们自定义的测试拦截器实现类加入到我们前面写的delegate对象中,实现对真正测试的回绕拦截。
OK,下面是该注解的代码:
package org.linkinpark.junit.testjunit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface InterceptorClasses
{
public Class<?>[] values();
}
关于上面的代码,这里就不做多的赘述了。这个注解定义一个values方法,用这个注解属性来保存我们插入的拦截器接口的实现类。
这里我们来写2个实现类好了,一个是日志拦截,一个是时间统计拦截。2个测试类的代码如下: