`
panhf2003
  • 浏览: 2400 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Junit实现跨测试类排序执行测试用例

阅读更多

    先来说一下问题背景,大家知道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();

 

    有什么意见,欢迎各位大虾指正。

分享到:
评论

相关推荐

    java测试用例JUnit教程

    Java 测试用例 JUnit 教程 Java 是一种广泛使用的编程语言,而 JUnit 是 Java 测试框架中最流行的测试框架之一。JUnit 提供了一组丰富的断言方法,帮助开发者编写可靠的测试用例,从而提高代码质量。本教程旨在...

    测试用例 junit

    JUnit 还提供了各种辅助类和注解,如 `@Before` 和 `@After` 来设置测试前后的环境,`@Test` 用于标识测试方法,以及 `@Ignore` 如果暂时不想执行某个测试。 测试用例通常包含以下几个关键部分: 1. **测试目标**...

    RC.rar_JUnit_RC软件测试_junit测试用例_测试用例_软件测试

    压缩包中的"RC"文件可能是一个具体的测试类,其中包含了一个或多个测试用例。一个测试用例是一段代码,用于验证程序的一个特定行为或功能。例如,如果测试的是一个计算两数之和的方法,测试用例可能包括正数、负数、...

    软件测试基本路径法设计测试用例Junit单元测试归纳.pdf

    软件测试基本路径法设计测试用例Junit单元测试归纳 软件测试基本路径法设计测试用例是软件测试中的一种重要方法,它可以帮助测试人员设计更加全面的测试用例,提高测试的效率和有效性。本文将介绍基本路径法设计...

    JUnit+in+java+真正的测试用例实战

    `@Test`标记测试方法,`@Before`和`@After`分别在每个测试方法前和后执行,而`@BeforeClass`和`@AfterClass`则在整个测试类开始和结束时运行一次。 3. **断言(Assertions)**:断言是测试的核心,用来验证代码的...

    软件测试基本路径法设计测试用例Junit单元测试.pdf

    Junit提供了许多功能,包括测试用例的设计、执行和报告。 在本资源中,作者使用了Junit来测试Money类。作者首先安装了Java虚拟机JDK,然后配置了Junit环境。接着,作者创建了Money类,并编写了测试用例。 三、测试...

    自动饮料机Junit测试(软件测试与质量保证实验).rar

    通过这样的实验,学生将学习如何创建测试类,定义测试方法,使用Junit的断言来验证结果,并理解测试驱动开发(TDD)的理念,即先写测试再写实现。 总之,"自动饮料机Junit测试"实验是实践软件测试理念和技巧的有效...

    使用用例场景设计测试用例 测试 软件工程 JUnit

    6. **运行测试**:使用JUnit提供的测试运行器运行测试类,查看测试结果,如果有失败,根据错误信息定位并修复问题。 在实际项目中,我们还会利用Mock对象来模拟依赖关系,使测试更加独立和可控。例如,使用Mockito...

    Junit测试 简单用例

    本实例将深入探讨Junit测试的基本用法,包括简单单元测试和综合测试的实现。 一、Junit简介 Junit是由Ernst Berg和Kent Beck共同创建的开源项目,其主要目标是提供一个易于使用的接口来编写针对Java代码的测试用例...

    Junit 单元测试完整案例

    在案例中,你将看到如何创建测试类,定义测试方法,设置测试数据,以及如何利用JUnit和Spring/Struts提供的工具来编写高效的单元测试。通过分析和运行源码,你可以了解如何将这些概念应用于实际项目,提高代码质量。...

    junit测试案例程序

    在这个“junit测试案例程序”中,我们可以找到一些基本的JUnit测试用例示例,这对于初学者来说是极好的学习资源。 首先,JUnit的核心概念是测试类和测试方法。一个测试类通常对应于被测试的业务逻辑类,而测试方法...

    JUnit in java 真正的测试用例实战

    使用eclipse快速开发test Case: 如下图:右键选择你要测试的类,在新建中点击“JUnit 测试用例”,

    JUnit in java 真正的测试用例实战.doc

    在生成的测试类中,`fail("尚未实现");`表示该测试方法尚未编写具体测试逻辑,需要根据实际代码功能进行实现。例如,对于DAO层的测试,可能会模拟数据库操作,使用如Mockito等工具来模拟数据访问,然后检查方法的...

    什么样的测试用例才是好的用例

    自动化测试工具如Selenium、JUnit等能够帮助实现这一目标。 综上所述,编写好的测试用例需要考虑多方面因素,包括明确目标、完整性、可重复性、易读性、覆盖性、独立性、优先级、有效性、预期结果验证和自动化潜力...

    Junit4测试用例示例

    可以通过`@Suite`注解将多个测试类组织成一个测试套件,方便批量运行: ```java @RunWith(Suite.class) @Suite.SuiteClasses({CalculatorTest.class, MathOperationsTest.class}) public class TestSuite {} ``` 六...

    基于Junit的ATM测试

    为了实现这些测试,我们需要在JUnit测试类中编写`@Test`注解的方法,每个方法对应一个测试场景。这些方法通常会调用Server类中的方法,并使用断言库(如JUnit自带的assertEquals、assertTrue等)来验证结果是否符合...

    Junit_与Selenium结合运行多测试类多个用例时程序设计方案

    ### Junit与Selenium结合运行多测试类多个用例时程序设计方案 #### 1. 环境配置 为了能够顺利地使用JUnit与Selenium进行自动化测试,首先需要完成环境的搭建工作。以下步骤详细介绍了如何配置开发环境。 1. **...

    在Eclipse中使用JUnit4进行单元测试

    创建JUnit测试类是单元测试的第一步。在Eclipse中,右键点击项目,选择"New" &gt; "JUnit Test Case"。在此过程中,需要选择要测试的源代码类以及要测试的方法。测试类通常会继承自`org.junit.Test`注解的基类,并且每...

Global site tag (gtag.js) - Google Analytics