您的位置:首页 > 其它

了解如何通过Windows Live Writer发布博客园博文

2011-06-09 13:46 441 查看
    先来说一下问题背景,大家知道Junit本来是做单元测试的,但是由于项目需要,希望把Junit的所有case按指定顺序执行,以达到自动化集合测试的效果。但是每个项目成员写的case分散在不同的测试类里,这样就涉及跨测试类如何按指定顺序执行的问题。查看spring和Junit自己提供的Runner发现,都需要指定某个测试类,即使有多个测试类的情况,其实内部实现也是给每个测试类分配一个Runner依次执行,无法打乱各测试类case的执行顺序。在这样的需求下,花了点时间自己实现了一个Runner来实现跨测试类排序执行测试用例。

 

    基本思路:把所有测试类的test方法导入Runner,通过Annotation给每个test方法标记一个顺序位,按指定方法排序后依次执行,达到系统自动化集合测试效果。

 

    以下为Runner主要代码:

 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.internal.runners.MethodRoadie;
import org.junit.internal.runners.TestClass;
import org.junit.internal.runners.TestMethod;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.RunNotifier;
import org.springframework.test.context.TestContextManager;

/**
* JUNIT执行器:多个class的情况允许跳跃执行
* 例如
* CLASS A : METHODA1,METHODA2
* CLASS B : METHODB1,METHODB2
* 允许如下执行顺序:METHODA1,METHODB1,METHODA2,METHODB2
* @author panhf2003
*
*/
class ClassesMethodsRunner extends Runner implements Filterable, Sortable {

private final List<Method> fTestMethods;
private static final Log logger = LogFactory.getLog(ClassesMethodsRunner.class);

private final TestContextManager testContextManager;
/**
* 构造器
* @param klass 需要执行的Class列表
* klass为空抛出IllegalArgumentException
*/
public ClassesMethodsRunner(Class<?>[] klass) {
if (klass == null || klass.length == 0) {
throw new IllegalArgumentException();
}
List<TestClass> temp = new ArrayList<TestClass>();
fTestMethods = new ArrayList<Method>();
for (Class<?> class1 : klass) {
TestClass testClass = new TestClass(class1);
temp.add(testClass);
fTestMethods.addAll(testClass.getAnnotatedMethods(org.junit.Test.class));
}
if (logger.isDebugEnabled()) {
logger.debug("ClassesMethodsRunner constructor called over.");
}
testContextManager = new TestContextManager(klass[0].getSuperclass());
}

/* (non-Javadoc)
* @see org.junit.runner.Runner#getDescription()
*/
@Override
public Description getDescription() {
Description spec= Description.createSuiteDescription("ClassesMethodsRunner");
List<Method> testMethods= fTestMethods;
for (Method method : testMethods)
spec.addChild(methodDescription(method));
return spec;
}

/* (non-Javadoc)
* @see org.junit.runner.Runner#run(org.junit.runner.notification.RunNotifier)
*/
@Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}

/* (non-Javadoc)
* @see org.junit.runner.manipulation.Filterable#filter(org.junit.runner.manipulation.Filter)
*/
public void filter(Filter filter) throws NoTestsRemainException {
...
}

/* (non-Javadoc)
* @see org.junit.runner.manipulation.Sortable#sort(org.junit.runner.manipulation.Sorter)
*/
public void sort(final Sorter sorter) {
...
}
protected Description methodDescription(Method method) {
return Description.createTestDescription(method.getDeclaringClass(), method.getName(), method.getAnnotations());
}
protected void runMethods(final RunNotifier notifier) {
for (Method method : fTestMethods)
invokeTestMethod(method, notifier);
}
/**
* 触发test方法
* @param method
* @param notifier
*/
protected void invokeTestMethod(Method method, RunNotifier notifier) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking test method [" + method.toGenericString() + "]");
}
Description description= methodDescription(method);
Object test;
try {
test= createTest(method);
} catch (InvocationTargetException e) {
notifier.testAborted(description, e.getCause());
return;
} catch (Exception e) {
notifier.testAborted(description, e);
return;
}
TestMethod testMethod= new TestMethod(method, getTestClass(method));
new MethodRoadie(test, testMethod, notifier, description).run();
}
/**
* @param method
* @return
*/
protected TestClass getTestClass(Method method) {
return new TestClass(method.getDeclaringClass());
}
/**
* 构造测试对象,加载配置文件数据
* @param method
* @return
* @throws Exception
*/
protected Object createTest(Method method) throws Exception {
Object testInstance = getTestClass(method).getConstructor().newInstance();
testContextManager.prepareTestInstance(testInstance);
return testInstance;
}
}

     外层再提供一个排序包装类来按指定顺序执行各test方法:

 

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.apache.log4j.Logger;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.springframework.util.ClassUtils;

/**
* 按指定顺序执行所有测试方法
* @author panhf2003
*
*/
public class CompositeTester {

/**
* 是否使用自带Runner
*/
private boolean useCustomRunner;
/**
* 需要执行test方法的class数组
*/
private String[] classNames;
private static Logger logger = Logger.getLogger("junit");
/**
* 构造
*/
public CompositeTester(boolean useCustomRunner, String[] classNames) {
this.useCustomRunner = useCustomRunner;
this.classNames = classNames;
}
/**
* 执行组合Case
* @return 是否存在失败Case
*/
public boolean runCompositeTest() {
boolean fail = false;
List<Failure> failures = new ArrayList<Failure>();
Result result = null;
if (useCustomRunner) {
// 使用自定义Runner 覆盖原SpringJUnit4ClassRunner
result = getJUnitCore().run(new MyRequest(getClazz(classNames)).sortWith(sorter()));
} else {
// 使用自带Runner
result = getJUnitCore().run(Request.classes("CompositeTester", getClazz(classNames)).sortWith(sorter()));
}
logger.info("共执行"+result.getRunCount()+"个test case,耗时:"+result.getRunTime()
+"ms,"+"失败case共"+result.getFailureCount()+"个,以下为失败case详细信息:");
failures.addAll(result.getFailures());
for (Failure failure : failures) {
fail = true;
logger.error(failure.toString(),failure.getException());
System.err.println(failure);
}
return fail;
}
private Class[] getClazz(String[] classNames) {
Class[] clazz = new Class[classNames.length];
for (int i = 0; i < clazz.length; i++) {
try {
clazz[i] = Class.forName(classNames[i]);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return clazz;
}
/**
* eclipse junit插件效果还原
* TODO 未完成
* @return
*/
private JUnitCore getJUnitCore() {
JUnitCore unitCore = new JUnitCore();
// TODO
return unitCore;
}

private Comparator<Description> sorter() {
return new Comparator<Description>() {
public int compare(Description o1, Description o2) {
Order order1 = o1.getAnnotation(Order.class);
if (order1 == null) {
return 0;
}
Order order2 = o2.getAnnotation(Order.class);
if (order2 == null) {
return 0;
}
return order1.value()==order2.value()?0:(order1.value()<order2.value()?-1:1);
}
};
}
private static class MyRequest extends Request {

private final Class<?>[] fClasses;
MyRequest(Class<?>... classes) {
fClasses = classes;
}

/* (non-Javadoc)
* @see org.junit.runner.Request#getRunner()
*/
@Override
public Runner getRunner() {
return new ClassesMethodsRunner(fClasses);
}
}
}

     另外,自定义Annotation就不贴出来了,这样,只要在每个test方法上标记Order顺序,就可以跨测试类按指定顺序执行测试case了,从而达到了自动化测试的效果:

 

CompositeTester compositeTester = new CompositeTester(true, classNames);

compositeTester.runCompositeTest();

 

    有什么意见,欢迎各位大虾指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐