- 浏览: 243837 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
Wangwei86609:
非常好的规则引擎框架,支持决策树和多线程运行规则https:/ ...
规则引擎 -
xl6861989:
2 楼正确
Maven中<dependencies>节点和<dependencyManagement>节点的区别 -
cuitengfei2006x:
抄别人的有意思吗
hadoop MapReduce join -
rainbow_小春:
写的真心赞,向你学习,学着自己写一些东西
hadoop MapReduce join -
zsx0321:
outputStreamReader 应改为OutputStr ...
从装饰者模式的理解说JAVA的IO包
概述
Junit是我们在单元测试中最常用的工具
包之一, 虽然该工具包十分简洁, 而且随后市面上也出现了各种测试工具和测试框架, 但是依然难撼其在单元测试领域的王者地位.
Junit4.x Runner剖析
junit3.x和junit4.x是两个非常不同的版本, 不能简单的理解为是后者是前者的一个升级, 二者的内部实现有很大的不同。 这里只针对junit4.x以后的版本。
所有的testcase都是在Runner下执行的,可以将Runner理解为junit运行的容器,默认情况下junit会使用
JUnit4ClassRunner作为所有testcase的执行容器。如果要定制自己的junit,则需要实现自己的Runner,最简单的办法就是
从Junit4ClassRunner继承,
spring-test,unitils这些框架就是采用这样的做法。如在spring中是SpringJUnit4ClassRunner,在
unitils中是UnitilsJUnit4TestClassRunner,一般我们的testcase都是在通过eclipse插件
来执行的,eclipse的junit插件会在执行的时候会初始化指定的Runner。初始化的过程可以在ClassRequest中找到:
1. @Override
2. public Runner getRunner() {
3. return buildRunner(getRunnerClass(fTestClass));
4. }
5.
6. public Runner buildRunner(Class< ? extends Runner> runnerClass) {
7. try {
8. return runnerClass.getConstructor(Class.class).newInstance(new Object[] { fTestClass });
9. } catch (NoSuchMethodException e) {
10. String simpleName= runnerClass.getSimpleName();
11. InitializationError error= new InitializationError(String.format(
12. CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName));
13. return Request.errorReport(fTestClass, error).getRunner();
14. } catch (Exception e) {
15. return Request.errorReport(fTestClass, e).getRunner();
16. }
17. }
18.
19. Class< ? extends Runner> getRunnerClass(final Class< ?> testClass) {
20. if (testClass.getAnnotation(Ignore.class) != null)
21. return new IgnoredClassRunner(testClass).getClass();
22. RunWith annotation= testClass.getAnnotation(RunWith.class);
23. if (annotation != null) {
24. return annotation.value();
25. } else if (hasSuiteMethod() && fCanUseSuiteMethod) {
26. return AllTests.class;
27. } else if (isPre4Test(testClass)) {
28. return JUnit38ClassRunner.class;
29. } else {
30. return JUnit4ClassRunner.class;
31. }
32. }
这里的局部变量fTestClass是当前的testcase类.通过getRunner()方法可以获取Runner,该Runner默认情况下是
Junit4ClassRunner, 当然也可以是自己的Runner,
只要从Runner继承即可,getRunnerClass()是取得具体的Runner
class的方法,在junit4.x中最简单的方式就是通过注解@RunWith来获取.所以要定制的话,最方便的做法就是通过@RunWith指定定
制的Runner, Spring-test, Unitils都是这么干的^_^
下面来看JUnit4ClassRunner的构造器:
1. public JUnit4ClassRunner(Class< ?> klass) throws InitializationError {
2. fTestClass= new TestClass(klass);
3. fTestMethods= getTestMethods();
4. validate();
5. }
JUnit4ClassRunner没有默认的构造器, 从构造器中我们可以看出, 它需要一个参数,
这个参数就是我们当前要运行的testcaseclass, Runner拿到了要执行的testcase类之后,
就可以进一步拿到需要执行的测试方法, 这个是通过注解拿到的:
1. protected List getTestMethods() {
2. return fTestClass.getTestMethods();
3. }
4.
5. List getTestMethods() {
6. return getAnnotatedMethods(Test.class);
7. }
8.
9. public List getAnnotatedMethods(Class< ? extends Annotation> annotationClass) {
10. List results= new ArrayList();
11. for (Class< ?> eachClass : getSuperClasses(fClass)) {
12. Method[] methods= eachClass.getDeclaredMethods();
13. for (Method eachMethod : methods) {
14. Annotation annotation= eachMethod.getAnnotation(annotationClass);
15. if (annotation != null && ! isShadowed(eachMethod, results))
16. results.add(eachMethod);
17. }
18. }
19. if (runsTopToBottom(annotationClass))
20. Collections.reverse(results);
21. return results;
22. }
初始化完成之后, 就可以根据拿到的Runner, 调用其run方法,执行所有的测试方法了:
1. @Override
2. public void run(final RunNotifier notifier) {
3. new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
4. public void run() {
5. runMethods(notifier);
6. }
7. }).runProtected();
8. }
9.
10. protected void runMethods(final RunNotifier notifier) {
11. for (Method method : fTestMethods)
12. invokeTestMethod(method, notifier);
13. }
14.
15. protected void invokeTestMethod(Method method, RunNotifier notifier) {
16. Description description= methodDescription(method);
17. Object test;
18. try {
19. test= createTest();
20. } catch (InvocationTargetException e) {
21. notifier.testAborted(description, e.getCause());
22. return;
23. } catch (Exception e) {
24. notifier.testAborted(description, e);
25. return;
26. }
27. TestMethod testMethod= wrapMethod(method);
28. new MethodRoadie(test, testMethod, notifier, description).run();
29. }
这里很多地方都利用了线程技术
, 可以忽略不管, 最终都是要通过反射拿到需要执行的测试方法并调用, 最终的调用在MethodRoadie中:
1. public void run() {
2. if (fTestMethod.isIgnored()) {
3. fNotifier.fireTestIgnored(fDescription);
4. return;
5. }
6. fNotifier.fireTestStarted(fDescription);
7. try {
8. long timeout= fTestMethod.getTimeout();
9. if (timeout > 0)
10. runWithTimeout(timeout);
11. else
12. runTest();
13. } finally {
14. fNotifier.fireTestFinished(fDescription);
15. }
16. }
17.
18. public void runTest() {
19. runBeforesThenTestThenAfters(new Runnable() {
20. public void run() {
21. runTestMethod();
22. }
23. });
24. }
25.
26. public void runBeforesThenTestThenAfters(Runnable test) {
27. try {
28. runBefores();
29. test.run();
30. } catch (FailedBefore e) {
31. } catch (Exception e) {
32. throw new RuntimeException("test should never throw an exception to this level");
33. } finally {
34. runAfters();
35. }
36. }
37.
38. protected void runTestMethod() {
39. try {
40. fTestMethod.invoke(fTest);
41. if (fTestMethod.expectsException())
42. addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName()));
43. } catch (InvocationTargetException e) {
44. Throwable actual= e.getTargetException();
45. if (actual instanceof AssumptionViolatedException)
46. return;
47. else if (!fTestMethod.expectsException())
48. addFailure(actual);
49. else if (fTestMethod.isUnexpected(actual)) {
50. String message= "Unexpected exception, expected< " +fTestMethod.getExpectedException().getName() + "> but was< "
51. + actual.getClass().getName() + ">“;
52. addFailure(new Exception(message, actual));
53. }
54. } catch (Throwable e) {
55. addFailure(e);
56. }
57. }
spring-test应用参考
下面是使用spring-test的runner如何来写testcase, 将会有不少简化(推荐
懒人使用):
要测试的方法:
1. public class ExampleObject {
2.
3. public boolean getSomethingTrue() {
4. return true;
5. }
6.
7. public boolean getSomethingFalse() {
8. return false;
9. }
10. }
测试用例:
1. @RunWith(SpringJUnit4ClassRunner.class)
2. @ContextConfiguration(locations = { "classpath:/applicationContext.xml" })
3. public class ExampleTest {
4. @Autowired
5. ExampleObject objectUnderTest;
6.
7. @Test
8. public void testSomethingTrue() {
9. Assert.assertNotNull(objectUnderTest);
10. Assert.assertTrue(objectUnderTest.getSomethingTrue());
11. }
12.
13. @Test
14. @Ignore
15. public void testSomethingElse() {
16. Assert.assertNotNull(objectUnderTest);
17. Assert.assertTrue(objectUnderTest.getSomethingFalse());
18. }
19. }
xml配置:
1. < ?xml version="1.0" encoding="gb2312"?>
2. < !DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
3.
4.
5.
6.
如果是使用maven的话, pom.xml的配置:
1.
2.
3. junit
4. junit
5. 4.4
6.
7.
8. org.springframework
9. spring-test
10. 2.5.5
11.
12.
13. org.springframework
14. spring-beans
15. 2.5.4
16.
17.
18. org.springframework
19. spring-context
20. 2.5.4
21.
22.
需要注意的一点就是, 到spring2.5之后的版本对注解的支持才逐渐大面积的推广
开来, 因此使用的时候, 要注意spring的版本问题, 因为在我们的项目
中都是采用的2.0.7, 对于这个限制不免留下了一点遗憾.
实战
看了spring的SpringJUnit4ClassRunner, 不得不让人手痒, 希望能定制自己的Runner.当然需要使用到java的annotation的相关知识.下面是在实际项目中结合二者的一个实战。
应用场景是这样的:我有一个测试工具类(DataGenerator)用来帮助初始化测试数据
和清除测试数据。该工具类需要两个配置文件
,一个是数据源的配置文件,一个是用来初始化数据的excel数据表,我希望通过借助java的annotation和自定义Runner来实现这个功能。于是我写了下面的两个类, 一个是annotation类:
1. @Retention(RetentionPolicy.RUNTIME)
2. @Target( { ElementType.TYPE})
3. public @interface DataGeneratorConfig {
4. /**
5. * jdbc配置文件
6. *
7. * @return
8. */
9. String dbConfig() default "db.config";
10.
11. /**
12. * excel文件列表
13. *
14. * @return
15. */
16. String[] excelFiles() ;
17. }
很明显, 该类就是用来获取配置文件信息的。接下来是在junit运行起来之后, 且在执行测试方法之前根据配置文件初始化一些数据, 于是我从JUnit4ClassRunner继承, 写了下面的类:
1. public class DataGeneratorJUnit4ClassRunner extends JUnit4ClassRunner {
2.
3. public DataGeneratorJUnit4ClassRunner(Class< ?> clazz)
4. throws InitializationError {
5. super(clazz);
6. }
7.
8. @Override
9. public void run(RunNotifier notifier) {
10. // 在运行前对DataGenerator进行初始化
11. initGenerator();
12. super.run(notifier);
13. }
14.
15. /**
16. * 初始化DataGenerator
17. */
18. private void initGenerator() {
19. Class< ?> clazz = getTestClass().getJava
Class();
20. while (clazz != null) {
21. DataGeneratorConfig annotation = clazz
22. .getAnnotation(DataGeneratorConfig.class);
23.
24. if (annotation != null) {
25. String dbConfig = annotation.dbConfig();
26. String[] excelFiles = annotation.excelFiles();
27.
28. try {
29. DataGenerator.initCache(getAbsoluteExcelPaths(excelFiles),
30. getAbsolutePath(dbConfig));
31. } catch (Exception e) {
32. throw new RuntimeException(”使用注解初始化DataGenerator失败”, e);
33. }
34. break;
35. }
36.
37. clazzclazz = clazz.getSuperclass();
38. }
39. }
40.
41. /**
42. * 取得excel文件绝对路径
43. * @param excelPaths
44. * @return
45. */
46. private String[] getAbsoluteExcelPaths(String[] excelPaths) {
47. String[] realPaths = new String[excelPaths.length];
48. for (int i = 0; i < excelPaths.length; i++) {
49. realPaths = getAbsolutePath(excelPaths);
50. }
51. return realPaths;
52. }
53.
54. /**
55. * 根据文件名取得文件绝对路径
56. *
57. * @param fileName
58. * @return
59. */
60. private String getAbsolutePath(String fileName) {
61. return DataGeneratorJUnit4ClassRunner.class.getClassLoader().getResource(fileName)
62. .getFile();
63. }
64. }
就这样我就可以借助annotation来完成初始化了, 在需要用到DataGenerator的testcase, 我可以这样写:
1. @RunWith(DataGeneratorJUnit4ClassRunner.class)
2. @DataGeneratorConfig(dbConfig = "config.properties", excelFiles = "xxx/yyy.xls")
就这么简单, 再也不需要写java代码来进行初始化了, 通过配置就可以搞定.
小结
如果你有一些特殊的测试工具需要与Junit结合的话,
一般都可以通过定制自己的JunitRunner加入进来.比如这里将DataGenerator与Junit整合,
spring也是一个很好的例子,他就是在junit的Runner中完成了spring的ApplicationContext初始化工作,
而不需要我们手动来处理.
发表评论
-
bash脚本
2012-02-01 15:27 918#!/bin/bash source /etc/profil ... -
sql。导出数据
2011-06-29 16:32 910group by为了统计 LOAD DATA L ... -
java NAN
2011-02-18 16:01 1566今天调试程序时,意外的发现java的浮点数中也有NaN,留 ... -
mysql5 unsigned 相减出现补数 溢出 解决办法
2011-01-11 14:35 18762010-08-17 17:36 在网上查 ... -
HashMap
2010-12-30 21:12 924Iterator<Ent ... -
sed grep
2010-12-30 12:52 943[admin@consign020090 12]$ grep ... -
SQL统计
2010-12-29 14:32 979select cooperate_id,count(*) f ... -
sed grep
2010-12-10 13:16 974tail -fn 1000 20 ... -
JAVA内存模型和多线程
2010-11-29 16:23 1156先了解一下java内存模型 IBM:http://www.i ... -
文件,流
2010-11-11 13:08 966public String getFullFileNa ... -
关于Interger和Long的hashCode
2010-11-03 15:08 2072/** * Returns a hash ... -
java配置文件
2010-10-18 14:22 1112(2010-03-04 20:53:29) 转 ... -
1111
2010-10-18 13:30 0http://blog.chinaunix.net/u2/73 ... -
性能研究
2010-10-15 16:40 1047import java.util.Arrays; im ... -
<![CDATA[...]]>的用法 转载
2010-10-08 16:17 1682关于xml中的<![CDATA[...]]>的 ... -
java beanUtils 转载
2010-09-03 17:53 56111. 概述 c ... -
转载:性能调优
2010-07-16 14:46 895性能测试过程中,我 ... -
前端json数据生成
2010-06-08 16:15 1411TB.BBC.MyApplication.in ... -
String内部实现
2010-03-28 01:20 1312import java.applet.Applet; imp ... -
转载 java网络编程 注意点
2010-03-18 15:59 1610一. 网络程序运行过程中的常见异常及处理 ...
相关推荐
《单元测试之道Java版:使用JUnit》PDF 下载
单元测试是软件开发过程中的重要环节,它确保代码的各个模块按照预期工作,提高了软件质量并简化了维护。...通过不断的实践和学习,你可以更加熟练地运用Junit进行单元测试,提升开发效率和软件的可靠性。
单元测试之道(Java):使用JUnit进行单元测试。单元测试是提高代码质量的有效手段,但大部分开发人员由于种种原因都不乐意进行单元测试。
1. Junit简介:Junit是一款开源的、基于Java的测试框架,由Ernst Leifer和Kent Beck创建。它支持注解驱动的测试,让编写和运行测试变得极其便捷。 2. 注解使用:`@Test`注解用于标记测试方法,`@Before`和`@After`...
2. **JUnit框架介绍**:JUnit是Java的单元测试框架,提供了编写和运行测试用例的API。书中将详细介绍JUnit的安装、配置,以及如何创建和运行第一个测试。 3. **断言与异常测试**:断言是单元测试的核心,用于检查...
JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。 [1] JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是...
JUnit是Java领域最广泛使用的单元测试框架之一,它极大地简化了测试代码的编写。Runner是JUnit中的一个重要概念,它是执行测试的核心组件。本篇文章将深入到JUnit的源码中,探讨Runner的实现原理,帮助开发者更好地...
通过这样的实验,学生将学习如何创建测试类,定义测试方法,使用Junit的断言来验证结果,并理解测试驱动开发(TDD)的理念,即先写测试再写实现。 总之,"自动饮料机Junit测试"实验是实践软件测试理念和技巧的有效...
单元测试通常由开发者自己编写,以确保自己的代码能够正确地工作。 知识点2:JUnit框架 JUnit是一个流行的Java单元测试框架,它提供了一些基本的断言方法,如assertEquals、assertTrue、assertFalse等,以便开发者...
2. 高度可扩展性:Junit提供了一个自动化的测试框架,允许开发者轻松地扩展测试用例。 3. 高度可靠性:Junit提供了一个统一的测试报告,允许开发者快速查看测试结果。 Junit单元测试的缺点包括: 1. 学习曲线陡峭...
JUnit4源码的完整版本包含了整个框架的实现细节,对于理解其工作原理、学习测试驱动开发(TDD)以及进行自定义扩展非常有帮助。 1. **JUnit核心概念**: - **Test Case**:在JUnit4中,测试用例是通过继承`org....
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者编写可重复运行的测试用例,以确保代码的正确性和稳定性。以下是对JUnit单元测试框架的一些详细解释: 1. **JUnit简介**: JUnit是一个开源的、基于...
简单介绍了Junit的安装过程与实例应用。应用的问题是软件测试中的佣兵问题,整个文档中有代码及测试结果,可以更好地帮助学生了解Junit单元测试中的作用。
6. **扩展性**:JUnit允许自定义规则(Rule)和监听器(Listener),通过扩展实现更复杂的功能,如使用Mockito进行依赖注入的模拟测试。 7. **断言**:JUnit提供了丰富的断言方法,如assertEquals()、assertTrue()...
《单元测试之道Java版-使用JUnit》是一本深入讲解如何在Java开发中运用JUnit进行单元测试的专业指南。单元测试是软件开发过程中的重要环节,它能够确保代码的正确性、可维护性和稳定性。JUnit作为Java领域最流行的...
JUnit作为Java领域中最流行的单元测试框架之一,提供了强大的测试能力,帮助开发者编写高质量的代码。 #### 二、JUnit框架的内部工作机制 JUnit的核心设计理念是将测试逻辑与业务逻辑分离,通过一系列标准化的API...
junit3 junit4 api,单元测试的利器
单元测试是软件开发过程中的重要环节,它旨在验证代码的各个独立模块是否按照预期...通过阅读《单元测试之道Java版使用JUnit》这本书,你可以深入了解如何有效地使用JUnit进行单元测试,以及如何构建强大的测试框架。