先来说一下问题背景,大家知道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 提供了一组丰富的断言方法,帮助开发者编写可靠的测试用例,从而提高代码质量。本教程旨在...
需要将 JUnit4 单元测试包引入项目中,然后在 Eclipse 的 Package Explorer 中右键点击 Calculator 类,选择“New à JUnit Test Case”,系统将自动生成一个新类 CalculatorTest,里面包含一些空的测试用例。...
JUnit 还提供了各种辅助类和注解,如 `@Before` 和 `@After` 来设置测试前后的环境,`@Test` 用于标识测试方法,以及 `@Ignore` 如果暂时不想执行某个测试。 测试用例通常包含以下几个关键部分: 1. **测试目标**...
压缩包中的"RC"文件可能是一个具体的测试类,其中包含了一个或多个测试用例。一个测试用例是一段代码,用于验证程序的一个特定行为或功能。例如,如果测试的是一个计算两数之和的方法,测试用例可能包括正数、负数、...
软件测试基本路径法设计测试用例Junit单元测试归纳 软件测试基本路径法设计测试用例是软件测试中的一种重要方法,它可以帮助测试人员设计更加全面的测试用例,提高测试的效率和有效性。本文将介绍基本路径法设计...
`@Test`标记测试方法,`@Before`和`@After`分别在每个测试方法前和后执行,而`@BeforeClass`和`@AfterClass`则在整个测试类开始和结束时运行一次。 3. **断言(Assertions)**:断言是测试的核心,用来验证代码的...
Junit提供了许多功能,包括测试用例的设计、执行和报告。 在本资源中,作者使用了Junit来测试Money类。作者首先安装了Java虚拟机JDK,然后配置了Junit环境。接着,作者创建了Money类,并编写了测试用例。 三、测试...
通过这样的实验,学生将学习如何创建测试类,定义测试方法,使用Junit的断言来验证结果,并理解测试驱动开发(TDD)的理念,即先写测试再写实现。 总之,"自动饮料机Junit测试"实验是实践软件测试理念和技巧的有效...
6. **运行测试**:使用JUnit提供的测试运行器运行测试类,查看测试结果,如果有失败,根据错误信息定位并修复问题。 在实际项目中,我们还会利用Mock对象来模拟依赖关系,使测试更加独立和可控。例如,使用Mockito...
本实例将深入探讨Junit测试的基本用法,包括简单单元测试和综合测试的实现。 一、Junit简介 Junit是由Ernst Berg和Kent Beck共同创建的开源项目,其主要目标是提供一个易于使用的接口来编写针对Java代码的测试用例...
在案例中,你将看到如何创建测试类,定义测试方法,设置测试数据,以及如何利用JUnit和Spring/Struts提供的工具来编写高效的单元测试。通过分析和运行源码,你可以了解如何将这些概念应用于实际项目,提高代码质量。...
在这个“junit测试案例程序”中,我们可以找到一些基本的JUnit测试用例示例,这对于初学者来说是极好的学习资源。 首先,JUnit的核心概念是测试类和测试方法。一个测试类通常对应于被测试的业务逻辑类,而测试方法...
使用eclipse快速开发test Case: 如下图:右键选择你要测试的类,在新建中点击“JUnit 测试用例”,
在生成的测试类中,`fail("尚未实现");`表示该测试方法尚未编写具体测试逻辑,需要根据实际代码功能进行实现。例如,对于DAO层的测试,可能会模拟数据库操作,使用如Mockito等工具来模拟数据访问,然后检查方法的...
自动化测试工具如Selenium、JUnit等能够帮助实现这一目标。 综上所述,编写好的测试用例需要考虑多方面因素,包括明确目标、完整性、可重复性、易读性、覆盖性、独立性、优先级、有效性、预期结果验证和自动化潜力...
可以通过`@Suite`注解将多个测试类组织成一个测试套件,方便批量运行: ```java @RunWith(Suite.class) @Suite.SuiteClasses({CalculatorTest.class, MathOperationsTest.class}) public class TestSuite {} ``` 六...
为了实现这些测试,我们需要在JUnit测试类中编写`@Test`注解的方法,每个方法对应一个测试场景。这些方法通常会调用Server类中的方法,并使用断言库(如JUnit自带的assertEquals、assertTrue等)来验证结果是否符合...
### Junit与Selenium结合运行多测试类多个用例时程序设计方案 #### 1. 环境配置 为了能够顺利地使用JUnit与Selenium进行自动化测试,首先需要完成环境的搭建工作。以下步骤详细介绍了如何配置开发环境。 1. **...
创建JUnit测试类是单元测试的第一步。在Eclipse中,右键点击项目,选择"New" > "JUnit Test Case"。在此过程中,需要选择要测试的源代码类以及要测试的方法。测试类通常会继承自`org.junit.Test`注解的基类,并且每...