Junit4更新的版本很多,现在已经更新到4.10了。发生的变化也相当的明显,为了更好的使用测试,有必要去学习一下其新的功能。
总的来说,其变化还是比较多的。
于是整合,收集了一些网友的文章。综合起来看还是算比较完整了,更多的只有自己去探讨了,不过常用的已经够用。
junit4给我的印像:
1,你的方法不必以test开头命名,你可以在你喜欢的任何地方进行测试,前提是你有@Test在这个方法前注释。
2,加入的很多机制,比如Assumption(假设机制)等等。
3,加入了不少测试方法,比如数组测试,assertEquals(Object[] expected, Object[] actual)
4,套件测试,可以几个类一起进行测试,非常强大。
总之,它更多的是引进的不少注解的方法,比以前方便比如@Ignore可以忽略此方法, @Test(timeout=1)//测试运行时间 (单位:millisecond)
它不是junit3的升级,完全是另外一种版本。
更多详细看看下面的几位达人观点:
先看这个:http://blog.csdn.net/javaer617/article/details/6422230
如果写了一个类,想测试一下有没有bug,可以用main方法去测试。但是main方法缺点很多,不是理想的做单元测试的途径——方法不能一起运行,且测试结果多数要通过程序员自己观察才可以判定。
为了克服这些缺点,使单元测试更加简单方便,Junit是一个很好的选择。接下来,将会讲解下Junit4的使用。
要使用Junit4非常的简单,准备工作也非常方便。以Junit4.8.1为例,只需要添加junit-4.8.1.jar就可以使用junit的所有传统方法。
以下是一个小例子:
待测试类T:
- package com.ellis.junit4;
- public class T {
- public int add(int x, int y){
- return x+y;
- }
- }
junit4测试类TTest:
- package com.ellis.junit4.test;
- import static org.junit.Assert.*;
- import org.junit.Test;
- import com.ellis.junit4.T;
- public class TTest {
- @Test
- public void testAdd() {
- int z = new T().add(5, 3);
- assertEquals("z != 8", z, 8);
- }
- }
TTest.class里的testAdd方法用来测试T.class里的add方法。@Test代表这个方法为测试方法。如果有多个方法用@Test注释的话,那么多个方法中的每个方法都可以单独运行(运行一个方法的时候,不运行其它方法),这是用main方法测试所无法比拟的。运行单个@Test方法的操作是:选中该方法名,然后点run。
TTest.class里的assertEquals()就是Junit的断言方法之一,它的作用是判断z的值是否为8,如果z不等于8的话junit将会报错,"z !=8"是自己写的用于与该错误提示一起提示出来字符串,使错误原因更加直观。assert*()方法有很多,它们都是org.junit.Assert的静态方法,若要像TTest.class里那样使用,需要进行静态引入:import static org.junit.Assert.*。
传统的,也就是Junit3及其之前的assert*()方法有很多,在这里就不一一介绍。这里主要介绍一下Junit4里新增加的hamcrest断言assertThat()。
若要使用hamcrest的断言,还必须添加hamcrest-core-1.2.jar和hamcrest-library-1.2.jar这个两个jar包。
assertThat()有什么作用?使用assertThat()有什么好处呢?assertThat()比起传统的assert*()断言,在功能上并没有多大的不同,它最主要的优点是在于——1.它的语法更接近于口语,从而使代码写和看起来更加直观;2.一个assertThat()可以实现绝大部分常用的传统assert*()方法的功能。下面以用assertThat()代替assertEquals()为例,讲解一下assertThat()。
用assertThat()的TTest:
- package com.ellis.junit4.test;
- import static org.junit.Assert.*;
- import static org.hamcrest.Matchers.*;
- import org.junit.Test;
- import com.ellis.junit4.T;
- public class TTest {
- @Test
- public void testAdd() {
- int z = new T().add(5, 3);
- assertThat(z, is(8));
- assertThat("wrong", z, allOf(greaterThan(5), lessThan(10)));
- assertEquals("z != 8", z, 8);
- }
- }
如上面代码所示assertThat(z, is(8))的作用是判断z的值是否为8(z is 8 or not ?),它的作用和assertEquals(z, 8)完全一样。is()其实是org.hamcrest.Matchers的静态方法,若要如此使用必须先进行静态引入import static org.hamcrest.Matchers.*。
上面代码中的assertThat(z, allof(greaterThan(5), lessThan(10)))的作用是判断z是否都allof()里面的条件,allof()里的条件是大于5(greaterThan(5))、小于3(lessThan(10))。
assertThat()常用的方法还有:
a)
assertThat( n, allOf( greaterThan(1), lessThan(15) ) ); n满足allof()里的所有条件
assertThat( n, anyOf( greaterThan(16), lessThan(8) ) );n满足anyOf()里的任意条件
assertThat( n, anything() ); n是任意值(任意值都可以通过测试)
assertThat( str, is( "ellis" ) ); str是is()里的内容
assertThat( str, not( "ellis" ) ); str不是not()里的内容
b)
assertThat( str, containsString( "ellis" ) ); str包含containsString()里的内容
assertThat( str, endsWith("ellis" ) ); str以endsWith()里的内容结尾
assertThat( str, startsWith( "ellis" ) ); str以startsWith()里的内容开始
assertThat( n, equalTo( nExpected ) ); n与equalTo()里的内容相等
assertThat( str, equalToIgnoringCase( "ellis" ) ); str忽略大小写后与equalToIgnoringCase()里的内容相等
assertThat( str, equalToIgnoringWhiteSpace( "ellis" ) );str忽略空格后与equalToIgnoringWhiteSpace()里的内容相等
c)
assertThat( d, closeTo( 3.0, 0.3 ) );d接近于3.0,误差不超过0.3
assertThat( d, greaterThan(3.0) );d大于3.0
assertThat( d, lessThan (10.0) );d小于10.0
assertThat( d, greaterThanOrEqualTo (5.0) );d大于或等于5.0
assertThat( d, lessThanOrEqualTo (16.0) );d小于或等于16.0
d)
assertThat( map, hasEntry( "ellis", "ellis" ) );map里有一个名为ellis的key,其值为ellis
assertThat( iterable, hasItem ( "ellis" ) );iterable(例如List)里包含值ellis
assertThat( map, hasKey ( "ellis" ) );map有一个名为ellis的key
assertThat( map, hasValue ( "ellis" ) );map里包含一个值ellis
另外,还有如下这些常用注解,使测试起来更加方便:
1. @Ignore: 被忽略的测试方法
2. @Before: 每一个测试方法之前运行
3. @After: 每一个测试方法之后运行
4. @BeforeClass: 所有测试开始之前运行
5. @AfterClass: 所有测试结束之后运行
一、以下来源于:http://zhan.zhmy.com/archives/2006/79054.html
JUnit 是JAVA语言事实上的标准测试库。JUnit 4是三年以来最具里程碑意义的一次发布。它的新特性主要是针对JAVA5中的标记(annotation)来简化测试,而不是利用子类、反射或命名机制。本文将讲述如何使用JUnit 4,当前提是你最好具有JUnit的使用经验.
JUnit, 由Kent Beck?和 Erich Gamma开发,几乎是JAVA开发最重要的第三方工具。正如Martin Fowler 所说,“在软件开发领域,从来就没有如此少的代码起到了如此重要的作用“。由于JUnit,JAVA代码变得更健壮,更可靠,BUG也比以前更少。由于JUnit (由Smalltalk's的SUnit得来) 的出现,随后产生了许多xUnit的测试工具,如nUnit (.NET), pyUnit (Python), CppUnit (C++), dUnit (Delphi) 和其它不同平台及语言的测试相关的工具。
虽然JUnit也只是一个工具,但其产生的思想和技术却较其架构更意义重大。单元测试,测试先行的编程方式,测试驱动的开发方式,并非必须由JUNIT实现,也不一定要用SWing实现GUI界面。JUNIT最近的一次更新是在三年前,但它比其它大多数有BUG的框架都要健壮,更重要的是,JAVA一直在改进。现在JAVA支持泛型,枚举,可变长度参数,以及标记语言(开创了开发可重用框架的新局面)。
JUnit's的停滞不前使得那些想要变革的开发人员换其它测试工具.挑战者有Bill Venners的Artima SuiteRunner和Cedric Beust的TestNG.这些工具库虽然有值得推荐的功能,但没有任何一款的地位能与JUNIT相比,没有任何一款工具被其它业界产品如Ant, Maven, Eclipse广泛支持.因此Beck 和Gamma双开始利用JAVA5的新特性来开发新版的JUNIT,目的是利用JAVA5中的标记特性使得单元测试开发更容易。Beck说:“JUNIT4的主要目的是通过简化JUNIT的使用鼓励更多的开发人员写更多的测试”。虽然会与以前的版本兼容,但JUNIT4与从JUNIT1.0就开始的版本相比会有一个非常大的变化.
注意: 修改基本框架是一把双刃剑,虽然JUNIT4的目的是清晰的,但细节仍有许多不同,因此本文只是一个简单的介绍,并不是最终文档.
在JUNIT 4中,测试方法由@Test 标记说明,如下:
import org.junit.Test; import junit.framework.TestCase; public class AdditionTest extends TestCase { private int x = 1; private int y = 1; @Test public void testAddition() { int z = x + y; assertEquals(2, z); } }
使用标记的好处是你不用将所有测试方法命名为 testFoo()
,testBar()等等以"test"开头的方法。
这种命名机制最大的优点是更适合你的待测试类或方法名称,例如,你可以使用ListTEst.contains()测试List.contains()
;使用ListTest.addAll()测试List.add()等等.
TestCase还可以继续使用,但你没有必须再扩展为子类,只要你声明了@Test,你可以将测试方法放在任何类中,当然如要访问assert等方法,你必须要引用junit.Assert类,如下:
public class AdditionTest {
private int x = 1;
private int y = 1;
@Test
public void addition() {
int z = x + y;
Assert.assertEquals(2, z);
}
}
你也可以使用JDK5中的新特性(static import)使得跟以前版本一样简单:
public class AdditionTest {
private int x = 1;
private int y = 1;
@Test
public void addition() {
int z = x + y;
assertEquals(2, z);
}
}
这种方法测试受保护的方法非常容易,因为你可以在测试类中继承有受保护方法的类.
在JUnit 4中,你仍然可以在每个测试前初始化变量和配置环境,,然而,这些操作可以不用在Setup()中完成,你可以在初始化方法前面添加@Beforer 来表示,如下:
protected void initialize() {
System.setErr(new PrintStream(new ByteArrayOutputStream()));
inputDir = new File("data");
inputDir = new File(inputDir, "xslt");
inputDir = new File(inputDir, "input");
}
你也可以有多个方法标记有@Before,所有方法都会在每个测试之前执行。
清除环境与JUNIT3 差不多,在JUNIT3中使用tearDown()方法。在JUnit4中,你还可以使用@After标记来说明:
protected void disposeDocument() {
doc = null;
System.gc();
}
与 @Before一样,你也可以有多个标记有 @After的清除方法,每个都会在执行完每个测试后执行。
最后,你不需要在父类中明确调用这些初始化或清除方法.test runner会自动调用这些标记的方法.子类中的@Before方法在父类的@Before方法之后执行(这与构造函数的执行顺序一样),而@After方法刚好相反,子类中的@After方法先执行.然而,多个@Before和@After方法的执行顺序就是未知的.
测试集范围的初始化
JUnit 4中引入了一项JUNIT3没有的新特性,类级别的setUp()和tearDown(),即在一个类的所有测试前执行初始化,并在所有测试完成后执行清除。
例如,一个测试类中的每个测试都要用到一个数据库连接或网络连接,或其它很耗资源初始化或释放的资源,用不着在每个测试方法前后进行操作,而只需要在测试类开始前后执行即可。下面的示例是使用第三方的库进行错误,在执行所有测试前将错误先重定向到非标准输出,然后在所有测试结束后再输出到需要的地方,这样就不会影响到测试过程中产生的其它信息。
// Xalan annoyingly logs to System.err. This hides System.err
// before each test and restores it after each test.
private PrintStream systemErr;
@BeforeClass
protected void redirectStderr() {
systemErr = System.err; // Hold on to the original value
System.setErr(new PrintStream(new ByteArrayOutputStream()));
}
@AfterClass
protected void tearDown() {
// restore the original value
System.setErr(systemErr);
}
上面的操作没有必须在每个测试前后执行。然而要注意的是,这种方法可能影响测试间的结果,如果一个测试改变了初始化的对象,而这个对象可能是其它测试的输入,那么测试的结果可能不正确,这种方法将依赖测试的顺序并可能引入BUG。当优化测试性能,并且当你改进了配置和基准测试后而仍然很慢时,如数据库连接或网络问题,你才需要考虑使用这种方法。只有这样,你才能每天执行多次测试。
异常测试是JUNIT4中的最大的改进,以前异常测试是通过try catch实现,当抛出异常时,在try的最后添加一条fail()语句实现.如下:
try {
int n = 2 / 0;
fail("Divided by zero!");
}
catch (ArithmeticException success) {
assertNotNull(success.getMessage());
}
}
这种方法不仅难看,而且造成无论成功或失败,代码覆盖工具都不能执行某些代码.而在JUnit 4中,你可以在要抛出异常的代码中添加标记来声明一个异常是期望的:
public void divideByZero() {
int n = 2 / 0;
}
如果没有异常抛出,上面的测试则会失败,如果你想知道异常的详细信息或其它情况,你还是要使用try catch才行。
时间测试
性能测试是单元测试中最头疼的问题,JUnit 4也未完全解决此问题, 你可以在JUNIT4的测试方法中添加一个时间参数。如果测试时间超过参数,则测试失败。如下,如果测试时间超过0.5秒,则此测试失败:
public void retrieveAllElementsInDocument() {
doc.query("//*");
}
JUnit 4 增加了两上断文方法用于比较数组:
public static void assertEquals(String message, Object[] expected, Object[] actual)
这两个方法采用最直接方法比较,如果数组长度相同,且每个对应的元素相同,则比较成功,否则不成功.参数为空的情况也作了考虑.
需要补充的地方
JUnit 4是一个非常基本的框架,还不是以前版本的升级。JUNIT3的开发人员会发现有些功能没有。
- 最大的特点就是没有GUI测试界面,当测试正确时是绿色条,而出错时红色的,你也可以在Eclipse中集成JUNIT使用,但JUNIT4既没有AWT也没有SWING的GUI测试界面;
- 另一个让人吃惊的是失败(期望错误)和错误(未预计的异常错误)没有明显区别,在JUNIT3中开发人员可以区分这两种情况,而在JUNIT4中不行;
- 最后一个特点是JUNIT中没有用于建立一堆测试类的suite()方法,取而代之的是,采用变长参数传递未知数量的测试给test runner。
二、以下来源于:http://fansofjava.iteye.com/blog/503709
下面列举一些改变之处:
1. 测试由原来的命名模式改变注解,即testXXX变为@Test。其中@Test还提供了额外的属性。如expected,表示期望抛出的异常,用法如下:
@Test(expected = NullPointerException.class)
public void unsupportedDBCheck() {
throw new NullPointerException();
}
由于上面得到期望抛出的异常,所以测试通过。
2. 数组比较改用Assert.assertArrayEquals
3. 套件测试也用注解替换,如下如示:
@RunWith(Suite.class)
@SuiteClasses({
Hello.class,
TheoryTest.class
})
public class SuiteClassesTest {
}
4. 通过@Ignore,可以忽略某个方法或整个类的测试
5. 增加了新特性-理论机制(Theory),这个特性听起来很迷惑人,作用是使得开发人员从开始的定义测试用例的阶段就可以通过参数集(理论上是无限个参数)对代码行为进行概括性的总结.开发人员都知道他们代码所想要实现的概括性的总的目的,理论使得他们只需要在一个地方就可以快速的指定这些目的,而不要将这些目的翻译成大量的独立的测试用例。用法如下所示:
@RunWith(Theories.class)
public class TheoryTest {
@DataPoint public static String name ="theories";
@DataPoint public static String password = "2009";
@Theory
public void outNameAndPassword(String name,String password){
System.out.println("name="+name+" password="+password);
}
}
输出为:
name=theories password=theories
name=theories password=2009
name=2009 password=theories
name=2009 password=2009
也就是说,方法的参数与定义的字段会把所有的组合情况全部考虑进去,虽然用到此特性的地方不多,但有时的确很有用。这样也不用逐个写断言语句,把测试数据集中在一个地方,方便管理。
6. 提供了新的特性-假设机制(Assumption).此特性使用了Hamcrest库的类.本来Hamcrest是一个单独的测试组件,Junit也集成了一部分,但是并没有完全包含。建议使用junit独立的JAR文件,再单独引入hamcrest包。
其实hamcrest的功能相当的强大,理解起来也非常的容易,是一个很不错的组件。不过好像并没发现其参考文档。它提供assertThat,assumeThat,assumeNotNull等假设语句,也提供is,not,both..and,either..or等用法,非常的灵活。如:
//断言"yy"不等于"xx"
assertThat("yy", is(not("xx")));
//abcdefg包含adcd或fsd
assertThat("abcdefg",anyOf(containsString("abcd"),containsString("fsd")));
其中assumeXXX的目的为,假设此条件成立,才执行后面的语句,否则跳出些测试方法,继续执行其它的测试。如:
//如果此假设语句不成立,则不会打印出this is assume
Assume.assumeThat(4,both(lessThanOrEqualTo(6)).and(greaterThan(3)));
System.out.println("this is assume");
7. @Before,@After,@BeforeClass,@AfterClass.这几个注解一看便知大概,@Before表示每个测试方法执行前执行一次,而@BeforeClass表示整个类测试前执行一次。不过需要注意的是,@BeforeClass,@AtferClass注解的方法必须是静态的。
8. Junit提供了新的核心运行类MaxCore,相对于以前的JunitCore运行机制,这个类有一系列的优点,如从未测试过的方法优先测试,剩下的测试中,以前测试失败的方法优先测试,再其次,运行快的优先于运行慢的方法。用法如官方例子如示:
public static class TwoUnEqualTests {
@Test
public void slow() throws InterruptedException {
Thread.sleep(100);
fail();
}
@Test
public void fast() {
fail();
}
}
@Test
public void rememberOldRuns() {
File maxFile = new File("history.max");
MaxCore firstMax = MaxCore.storedLocally(maxFile);
firstMax.run(TwoUnEqualTests.class);
MaxCore useHistory= MaxCore.storedLocally(maxFile);
List<Failure> failures= useHistory.run(TwoUnEqualTests.class)
.getFailures();
assertEquals("fast", failures.get(0).getDescription().getMethodName());
assertEquals("slow", failures.get(1).getDescription().getMethodName());
}
当然这个替代也是相对的,有时候还是可以继续用JUnitCore,MaxCore只是在JUnitCore基础上增加优先测试之类的规则,但是有些功能还是需要用JUnitCore。比如说,如果想在eclipse中不通过junit插件来运行单元测试,就需要JUnitCore:
public class ObjectToClassTest {
@Test
public void ObjectTest(){
int[] a={1,2};
int[] b={1,2};
Assert.assertArrayEquals(a,b);
}
public static void main(String[] args){
JUnitCore.main("com.junit.ObjectToClassTest");
}
}
当然这种方式针对的是类,不能限制到方法上,如果有多个类,只需要用空格把类隔开即可。
上面基本包含了Junit4.6之前的新特性,至于以后的新特性,会花专门的篇幅来讲解。
三、以下来源于:http://q.sohu.com/forum/5/topic/1531298
下面上三种基本的测试用例的程序编写:
1.基本测试
import static org.junit.Assert.*;
import org.apache.commons.logging.*;
import org.junit.*;
public class Test1{
private static Log log = LogFactory.getLog(Test1.class);
@Before//每个测试单元执行前执行该方法,方法级的前置,当然还有类级别的前置BeforeClass,只执行一次
public void init() {
log.info("init.....");
}
@After//每个测试单元执行后执行该方法,方法级的后置,当然还有类级别的后置AfterClass,只执行一次
public void destory() {
log.info("release resource");
}
public void functionOne() {
System.out.println("");
}
@Test(expected=NullPointerException.class)//指定抛出指定的异常
public void functionError() {
String test = null;
// assertNull(test);
test.toString();
}
@Test
public void Equals() {
String testPoint = new String("true");
String testTargt = new String("true1");
assertEquals(testPoint, testTargt);
}
@Ignore("此测试已被忽略")
@Test(timeout=1)//测试运行时间 (单位:millisecond)
public void timeout() {
String testPoint = new String("true");
String testTargt = new String("true1");
assertTrue("Pattern did not validate zip code", true);
}
}
2.套件测试
测试用例类1:Test1.java
public class Test1 {
@Test
public void test1(){
System.out.println("test1");
}
@Test
public void test2(){
System.out.println("test2");
}
}
测试用例类2:Test2.java
public class Test2 {
@Test
public void Tets3(){
System.out.println("test3");
}
@Test
public void Test4(){
System.out.println("test4");
}
}
测试套件类:
/**
* 在 JUnit 4 中,套件语义被两个新注释所替代。
* 第一个是 @RunWith,设计它是为了方便让不同的运行器
* (除了构建进框架的运行器)执行一个特别的测试类。
* JUnit 4 绑定一个叫做 Suite 的套件运行器,
* 必须在 @RunWith 注释中指定这个运行器。
* 不仅如此,还必须提供另一项叫做 @SuiteClasses 的注释,
* 它将一个意欲表示测试套件的类列表作为参数。
* @author JersoN
*
*/
@RunWith(Suite.class)
@SuiteClasses({Test1.class,Test2.class})
public class SuiteTest {
/*在这里还可以继续添加新的测试@Test*/
}
3.参数测试
参数测试代码要多一些
import java.util.*;
import org.apache.commons.logging.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)//指定此测试类使用测试时使用参数集进行测试
public class MainTest {
Log log = LogFactory.getLog(MainTest.class);
@Test
public void testEqual() {
assertTrue(source.equals(target));
}
@Test
public void testStringLength(){
//可以看到以往做参数测试得把测试的参数传递进来,当我们要测试多个参数的时候很麻烦,Juint4用了更好的实现解决这个问题,你可以把定义的多个参数用于不同的测试
assertTrue(source.length()==target.length());
}
@Parameters
public static Collection params() {
return Arrays.asList(new Object[][] {
{ "22101", "22101" },
{ "221x1", "22101" },
{ "22101-5150", "22101"},
{ "221015150", "221015150" }
});
}
/**
* 以下是为参数化测试所做的编码
*/
private String source;
private String target;
/**
* 参数化测试必须的构造函数
* @param source 对应参数集中的第一个参数
* @param target 对应参数集中的第二个参数
*/
public MainTest(String source,String target){
this.source = source;
this.target = target;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
}