了解如何通过Windows Live Writer发布博客园博文
2011-06-09 13:46
441 查看
先来说一下问题背景,大家知道Junit本来是做单元测试的,但是由于项目需要,希望把Junit的所有case按指定顺序执行,以达到自动化集合测试的效果。但是每个项目成员写的case分散在不同的测试类里,这样就涉及跨测试类如何按指定顺序执行的问题。查看spring和Junit自己提供的Runner发现,都需要指定某个测试类,即使有多个测试类的情况,其实内部实现也是给每个测试类分配一个Runner依次执行,无法打乱各测试类case的执行顺序。在这样的需求下,花了点时间自己实现了一个Runner来实现跨测试类排序执行测试用例。
基本思路:把所有测试类的test方法导入Runner,通过Annotation给每个test方法标记一个顺序位,按指定方法排序后依次执行,达到系统自动化集合测试效果。
以下为Runner主要代码:
外层再提供一个排序包装类来按指定顺序执行各test方法:
另外,自定义Annotation就不贴出来了,这样,只要在每个test方法上标记Order顺序,就可以跨测试类按指定顺序执行测试case了,从而达到了自动化测试的效果:
有什么意见,欢迎各位大虾指正。
基本思路:把所有测试类的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();
有什么意见,欢迎各位大虾指正。
相关文章推荐
- 了解如何通过Windows Live Writer发布博客园博文
- 了解如何通过Windows Live Writer发布博客园博文
- 如何使用Windows Live Writer在博客园发布文章
- 如何配置Windows Live Writer发布博客园随笔
- 如何用Windows Live Writer发布博文至51CTO博客
- 如何使用Windows Live Writer在博客园发布文章【博客园新手可以看看哦】
- 如何使用Windows Live Writer在博客园发布文章
- 【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结
- CSDN的Blog要如何配置才能够用Windows Live Writer发布带有图片的Post?
- 如何使用Windows Live Writer发布CSDN Blog
- CSDN的Blog要如何配置才能够用Windows Live Writer发布带有图片的Post?
- CSDN的Blog要如何配置才能够用Windows Live Writer发布带有图片的Post?
- 【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结
- 使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园教程
- 如何用 Windows Live Writer发布新浪博客及设置
- CSDN的Blog要如何配置才能够用Windows Live Writer发布带有图片的Post?
- [转]如何使用Windows Live Writer发布CSDN Blog
- 【转载】【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结
- CSDN的Blog要如何配置才能够用Windows Live Writer发布带有图片的Post?
- 使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园