现在的位置: 首页 > 综合 > 正文

JUnit 学习笔记 NetBeans篇

2018年04月09日 ⁄ 综合 ⁄ 共 8612字 ⁄ 字号 评论关闭
JUnit学习笔记(NetBeans篇)
 
一、简介
    JUnit是一款由Erich Gamma(《设计模式》的作者)和Kent Beck(极限编程的提出者)编写的开源的回归测试框架,供Java编码人员做单元测试之用。当前版本4.1,可以从www.junit.org网站上获得。与早期的JUnit 3相比,JUnit 4.1依赖于Java 5.0的新特性,因此无法兼容于jdk 1.4,可以说是一个全新的框架。
    这里使用的IDE是NetBeans 5.5中文版,由于NetBeans仅集成了junit 3.8.1,因此下面还需要下载和配置一下JUnit 4.1类库。请先从www.junit.org上获得junit4.1.zip文件,解压缩,然后找到其中的junit-4.1.jar,将该文件放到特定文件夹中备用,我放到了本机的F:/YPJCCK/JUnit/lib文件夹中。
二、创建项目
    下面打开NetBeans,然后点击菜单“文件”->“新建项目”或“新建项目”按钮,打开“新建项目”对话框:
请在“类别”中选择“常规”,再从“项目”中选择“Java应用程序”,然后点击“下一步”,进入“新建Java应用程序”对话框:
在这个对话框中需要设置项目的名称以及项目所在目录,我为自己的项目起名为JUnitTest,“项目位置”为F:/YPJCCK/JUnit/NetBeans,此外,请将“创建主类”前的对号去掉。然后点击“完成”。
    项目创建后,NetBeans会自动加载JUnit 3.8.1,但这并不是我们所要的,所以请从“项目”窗口中选择“JUnitTest”->“测试库”->“JUnit-junit-3.8.1.jar”:
点击鼠标右键,“删除”。然后再选中“测试库”,点击鼠标右键,选择“添加 JAR/文件夹”,打开对话框,找到之前存放在特定目录下的junit-4.1.jar文件,打开,此时JUnit 4.1环境就配置好了。
三、编写用于测试的JavaBean
    用于测试的JavaBean很简单,名为Book,只有id和name两个属性,这两个属性将分别用于两个用例当中。下面开始编写该JavaBean。
    请点击“文件”->“新建文件”或“新建文件”按钮,打开“新建文件”对话框:
确保“项目”选择的是JUnitTest,然后在“类别”中选中Java类,在“文件类型”中选中Java类,点击“下一步”,进入下一窗口:
设置类名为Book,包为net.test.unit.junit,设置完成后点击“完成”,修改代码如下:
package net.test.unit.junit;
      
public class Book {
   
           private String id = null;
           private String name = null;
   
    public String getId() {
               return id;
           }
   
    public void setId(String id) {
               this.id = id;
           }
   
    public String getName() {
               return name;
    }
   
    public void setName(String name) {
               this.name = name;
    }
   
}
至此,用于测试的JavaBean编写完成。
四、编写测试用例
    这里只用了一个类进行测试,名为BookTest。以前像这样的类是需要继承junit.framework.TestCase的,但由于JUnit 4.1充分利用了Java 5.0新增的注解功能,因此便无须再这样做了。当然,JUnit 4.1仍然提供对旧方式的支持,不过这里并不打算介绍。
    BookTest类包含两个用例,分别对应该类的caseId和caseName方法,即每个方法实现一个用例。与JUnit 3.8.1不同,在JUnit 4.1中不再强制要求方法名以test开头,而是允许随意命名,只要符合Java的命名规范就行,这里为了表明这点,特意用了case开头,但测试用例必须以@Test注解。此外,BookTest还有setUp和tearDown这两个方法,并分别使用@Before和@After来进行注解,前者在每个测试方法开始之前执行,多用来做初始化;后者在每个测试方法完成之后执行,多用来清理资源。注意,这两个方法的命名同样没有限制,且定义的数量也没有限制,只是必须用@Before和@After进行注解。另外,JUnit 4.1还提供了@BeforeClass和@AfterClass注解,功能与@Before和@After类似,但前者是用在所有用例执行之前做初始化、之后做清理,而后者是在每个用例执行之前做初始化、之后做清理。下面开始编写BookTest。
    请点击“文件”->“新建文件”,打开“新建文件”对话框:
确保“项目”选择的是JUnitTest,然后在“类别”中选中JUnit类,在“文件类型”中选中现有类的测试,点击“下一个”,进入下一窗口:
选择要测试的类,点击“完成”,修改代码如下:
package net.test.unit.junit;
 
import static org.junit.Assert.*;
 
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
 
public class BookTest {
   
    Book book = null;
   
    @Before
    public void setUp() throws Exception {
        System.out.println("测试开始!");
        book = new Book();
        System.out.println("book对象被初始化!");
    }
   
    @After
    public void tearDown() throws Exception {
        System.out.println("book对象将被清理!");
        book = null;
        System.out.println("测试结束!");
    }
   
    @Test
    public void caseId() {
        book.setId("001"); //设置id属性的值为001
        //使用Assert查看id属性的值是否为001
        assertEquals("001", book.getId());
        System.out.println("id属性被测试!");
    }
   
   @Test
    public void caseName() {
        book.setName("ASP"); //设置name属性的值为ASP
        //使用Assert查看name属性的值是否为JSP,这是个必然出现错误的测试
        assertEquals("JSP", book.getName());
        System.out.println("name属性被测试!");
    }
   
}
这里setUp和tearDown方法没什么好说的,就是执行了对book对象的初始化和清理,不过caseId和caseName需要说明一下。前者是在对book的id属性进行测试,首先赋值为”001”,然后使用assertEquals方法查看id属性中存放的值是否是期待的值,由于我的期待值也是”001”,所以执行后这个用例应该是成功的;后者则是对book的name属性进行测试,也是首先赋值为”ASP”,然后使用assertEquals方法查看其值是否是期待的,由于我特意将期待值设定为根本不可能的”JSP”,因此这个用例执行后会出现一个错误。
    关于assertEquals方法,是Assert类的一个静态方法。在程序开头有这样一行代码,“import static org.junit.Assert.*;”,利用了Java 5.0提供的静态导入将Assert类静态导入,因此我们在程序中可以直接使用Assert类的任何静态方法。下面简单介绍一下静态类org.junit.Assert。
    该类主要包含8类22个方法,如下:
    1.assertEquals(),8个重载,用来查看对象中存的值是否是期待的值,与字符串比较中使用的equals()方法类似;
    2.assertFalse()和assertTrue(),各2个重载,用来查看变量是是否为false或true,如果assertFalse()查看的变量的值是false则测试成功,如果是true则失败,assertTrue()与之相反;
    3.assertSame()和assertNotSame(),各2个重载,用来比较两个对象的引用是否相等和不相等,类似于通过“==”和“!=”比较两个对象;
    4.assertNull()和assertNotNull(),各2个重载,用来查看对象是否为空和不为空;
    5.fail (),2个重载,意为失败,用来抛出错误。我个人认为有两个用途:首先是在测试驱动开发中,由于测试用例都是在被测试的类之前编写,而写成时又不清楚其正确与否,此时就可以使用fail方法抛出错误进行模拟;其次是抛出意外的错误,比如要测试的内容是从数据库中读取的数据是否正确,而导致错误的原因却是数据库连接失败。
五、运行BookTest
    编写好BookTest后,就可以运行了。请在“项目”窗口中选中BookTest,点击鼠标右键,选择“运行文件”,此时“JUnit测试结果”窗口输出如下:
 
咦,怎么回事,为什么会无测试通过呢?难道是基于JUnit 4.1开发的测试用例没有继承junit.framework.TestCase,而NetBeans又只支持JUnit 3,因此无法运行吗?的确。那么我们应该怎么办呢?请在BookTest中增加如下代码:
    public static junit.framework.Test suite() {
        return new junit.framework.JUnit4TestAdapter(BookTest.class);
    }
然后重新运行BookTest,此时输出如下:
咦,怎么又失败了?别误会,请仔细看一下,“1个测试通过”,这说明BookTest已经被正确运行了。原来我们在新编写的suite方法中使用的junit.framework.JUnit4TestAdapter类可以使基于JUnit 4.1编写的测试用例运行于JUnit 3环境。至于那个错误,这可是预计之内的啊。如果不喜欢,可以将testName()方法中的”JSP”改成”ASP”,此时的输出如下图:
您会看到,“两个测试通过”,这说明已经没有错误了。
六、测试套件
    当有多个测试类需要同时进行测试时,应使用测试套件来完成该工作。
    点击“文件”->“新建文件”,打开“新建文件”对话框:
确保“项目”选择的是JUnitTest,然后在“类别”中选中JUnit类,在“文件类型”中选中测试套件,点击“下一步”,进入下一窗口:
修改“类名”为AllTests,“包”选择net.test.unit.junit,点击“完成”,然后修改代码如下:
    package net.test.unit.junit;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
   
@RunWith(Suite.class)
@Suite.SuiteClasses(BookTest.class)
public class AllTests {
        public static junit.framework.Test suite() {
            return new junit.framework.JUnit4TestAdapter(AllTests.class);
        }
}
这里suite方法无须再说,而类AllTests则使用@RunWith和@Suite.SuiteClasses进行注解,以作为测试程序入口/。将要测试的类BookTest作为@Suite.SuiteClasses注解的参数,然后将测试套件Suite作为参数设置给运行器@RunWith。下面就可以选中该文件运行了。
    这里注意一点,@Suite.SuiteClasses注解支持数组,例如:
        @Suite.SuiteClasses ({BookTest.class, BookTest2.class })
这样就可以一次运行多个测试类了。
七、命令行下
    前边介绍的运行方式都是基于NetBeans的,其实JUnit自身也提供了办法,可以在命令行下执行如下命令:
    java -cp junit-4.1.jar所在文件夹; org.junit.runner.JUnitCore
net.test.unit.junit.AllTests
如果要运行多个测试类,如下:
    java -cp junit-4.1.jar所在文件夹; org.junit.runner.JUnitCore
net.test.unit.junit.AllTests net.test.unit.BookTest
八、JUnit使用进阶
    @Ignore注解,忽略测试,用于忽略暂时不想运行的测试用例。以BookTest为例,在文件头部添加引用“import org.junit.Ignore;”,然后修改caseName方法:
    @Ignore
    @Test
    public void caseName() {
选中该文件运行,效果如下:
此时caseName()方法已经被忽略了。
    @Test注解的expected参数,异常测试,用于测试是否会抛出指定的异常,若抛出则为成功,反之为失败。请在BookTest中新增一个测试用例:
@Test(expected = ArithmeticException.class)
public void caseException() {
    int n = 2 / 0;
}
这个测试用例是以0为除数,运行效果如下:
成功!因为指定的ArithmeticException异常被抛出了。
    @Test注解的timeout参数,限时测试,用于限定测试用例耗费的时间,单位毫秒,如果测试用例没有在限定时间内完成则为失败,否则以测试用例的执行结果为准。请在BookTest中新增一个测试用例:
@Test(timeout=1000)
public void caseWhile() {
    for (;;) {
    }
}
这是一个死循环,1秒之后将被强制停止,运行效果如下:
由于超时,运行失败。
    @Parameters注解,参数化测试,用于对同一测试用例测试一组数据。请点击“文件”->“新建文件”,在“新建文件”对话框中选择“JUnit”->“空测试”,新建一个BookTest2,然后修改代码如下:
    package net.test.unit.junit;
   
import static org.junit.Assert.assertEquals;
   
import java.util.Arrays;
import java.util.Collection;
   
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
   
@RunWith(Parameterized.class)
public class BookTest2 {
   
        private String expectedId;
        private String targetId;
        private String expectedName;
        private String targetName;
   
        Book book = null;
   
        @Parameters
        public static Collection Result() {
            return Arrays.asList(new Object[][] {
                { "002", "001", "JSP", "ASP" },
                { "001", "001", "ASP", "ASP" }
            });
        }
   
        public BookTest2(String expectedId, String targetId, String expectedName, String targetName) {
            this.expectedId = expectedId;
            this.targetId = targetId;
            this.expectedName = expectedName;
            this.targetName = targetName;
        }
   
        @Before
        public void setUp() throws Exception {
            System.out.println("测试开始!");
            book = new Book();
            System.out.println("book对象被初始化!");
        }
   
        @After
        public void tearDown() throws Exception {
            System.out.println("book对象将被清理!");
            book = null;
            System.out.println("测试结束!");
        }
   
        @Test
        public void caseId() {
            book.setId(targetId); //设置id属性的值
            //使用Assert查看id属性的值
            assertEquals(expectedId, book.getId());
            System.out.println("id属性被测试!");
        }
   
        @Test
        public void caseNames() {
            book.setName(targetName); //设置name属性的值
            //使用Assert查看name属性的值
            assertEquals(expectedName, book.getName());
            System.out.println("name属性被测试!");
        }
   
        public static junit.framework.Test suite() {
            return new junit.framework.JUnit4TestAdapter(BookTest2.class);
        }
}
这个例子其实就是BookTest的扩展版,但在原基础上有几点变化:/
    首先是文件头部增加了一行代码:@RunWith(Parameterized.class),用来调用BookTest2类运行;
    其次是定义了一个用@Parameters注解的Result静态方法,该方法用来存放测试数据,本例存放了2组数据,每组4个;
    再次是定义了一个带参数的构造函数,其参数个数与每组测试数据的个数相等;
    最后是定义了expectedId等4个成员变量,用来传递测试数据到测试用例中。
下面执行BookTest2,运行效果如下:
测试用例运行了两遍,第一遍由于期待值和设定值不相等而失败,第二遍则运行成功。
九、版本
    《JUnit学习笔记 NetBeans篇》,2006年1月27日,JUnit版本3.8.1,IDE为NetBeans 4.1。
十、参考
    《JUnit 4 抢先看》;
    《单元测试利器 JUnit 4》;
    《全面认识JUnit 4的新特征》;
    《新版JUnit 4.0 抢先体验》;
    《JUnit重装上阵》等。

抱歉!评论已关闭.