Junit4使用详解(二)

时间:2022-02-23 16:45:46

JUnit4的用法介绍

Java 语言现在支持泛型、枚举、可变长度参数列表和注释,这些特性为可重用的框架设计带来了新的可能。
JUnit4利用 Java 5 的新特性(尤其是注释)的优势,使得单元测试比起用最初的 JUnit 来说更加简单。 

测试方法 @Test

以前所有版本的 JUnit 都使用命名约定和反射来定位测试。例如,下面的代码测试 1 + 1 等于 2:

 

Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest extends TestCase  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)public void testAddition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}


而在 JUnit4 中,测试是由 @Test 注释来识别的,如下所示:

Junit4使用详解(二)import org.junit.Test;
Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)@Test
Junit4使用详解(二)public void testAddition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}

使用注释来识别测试方法的优点是不再需要将所有的方法命名为 testAddition()、testXXX()的形式等等。
例如,下面的方法也可以工作:

Junit4使用详解(二)import org.junit.Test;
Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)@Test
Junit4使用详解(二)public void addition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)

使用这种方法的好处是:
a)允许我们遵循最适合的应用程序的命名约定。
我们可以将测试方法使用与被测试的类相同的名称(由开发组规范约定)。例如,LoginAction.login() 由 LoginActionTest.login()方法测试、LoginAction.check()由LoginActionTest.check()方法测试等等。
b)使用JUnit4后,测试用例类可以不继承TestCase类,所以我们也就可以扩展被测试类了。
这种方法使得测试受保护的方法非常容易,我们只要将测试用例类继承被测试类,就可以测试受保护方法了。 

@Before(SetUp)

JUnit 3 测试运行程序会在运行每个测试之前自动调用 setUp() 方法。该方法一般会初始化字段、准备数据等等。例如下面的 setUp() 方法,用于设定要加载的路由文件:

 

Junit4使用详解(二)public void setUp()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)


在 JUnit4 中,我们仍然可以在每个测试方法运行之前初始化字段或准备数据。然而,完成这些操作的方法不再需要叫做 setUp(),只要用 @Before 注释来指示该方法即可,如下所示:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void initialize()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)


JUnit4允许我们使用 @Before 来注释多个方法,这些方法都在每个测试之前运行:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void initialize()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)@Before
Junit4使用详解(二)public void prepareRetData() {
Junit4使用详解(二)//Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

 

@After(TearDown)

清除方法与初始化方法类似。在 JUnit3 中,我们要将方法命名为 tearDown() 才可以实现清除方法,但在JUnit4中,只要给方法添加@After标注即可。
例如:

 

Junit4使用详解(二)    @After
Junit4使用详解(二)public static void clearContext()  {
Junit4使用详解(二)ActionContext.getContext().put(StrutsStatics.HTTP_REQUEST, null);
Junit4使用详解(二)ActionContext.setContext(null);
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)


测试方法结束后清除为此测试用例准备的一些数据。
与 @Before 一样,也可以用 @After 来注释多个清除方法,这些方法都在每个测试之后运行。 
最后,我们不再需要显式调用在超类中的初始化和清除方法,只要它们不被覆盖,测试运行程序将根据需要自动为您调用这些方法。
超类中的 @Before 方法在子类中的 @Before 方法之前被调用(这反映了构造函数调用的顺序)。
@After 方法以反方向运行:子类中的方法在超类中的方法之前被调用。否则,多个 @Before 或 @After 方法的相对顺序就得不到保证。

 

@Before和@After小结

假设测试类中有如下方法定义:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void init() {Junit4使用详解(二)}
Junit4使用详解(二)@After
Junit4使用详解(二)public void destroy(){Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

则Before、After方法的执行流程如图所示:

浅谈TDD

测试驱动开发,它是敏捷开发的最重要的部分。方法主要是先根据客户的需求编写测试程序,然后再编码使其通过测试。在敏捷开发实施中,开发人员主要从两个方面去理解测试驱动开发。

a)在测试的辅助下,快速实现客户需求的功能。通过编写测试用例,对客户需求的功能进行分解,并进行系统设计。我们发现从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。 
b)在测试的保护下,不断重构代码,提高代码的重用性,从而提高软件产品的质量。可见测试驱动开发实施的好坏确实极大的影响软件产品的质量,贯穿了软件开发的始终。 
在测试驱动开发中,为了保证测试的稳定性,被测代码接口的稳定性是非常重要的。否则,变化的成本就会急剧的上升。所以,自动化测试将会要求您的设计依赖于接口,而不是具体的类。进而推动设计人员重视接口的设计,体现系统的可扩展性和抗变性。 


JUnit4的用法介绍

Java 语言现在支持泛型、枚举、可变长度参数列表和注释,这些特性为可重用的框架设计带来了新的可能。
JUnit4利用 Java 5 的新特性(尤其是注释)的优势,使得单元测试比起用最初的 JUnit 来说更加简单。 

测试方法 @Test

以前所有版本的 JUnit 都使用命名约定和反射来定位测试。例如,下面的代码测试 1 + 1 等于 2:

 

Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest extends TestCase  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)public void testAddition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

而在 JUnit4 中,测试是由 @Test 注释来识别的,如下所示:

 

Junit4使用详解(二)import org.junit.Test;
Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)@Test
Junit4使用详解(二)public void testAddition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

使用注释来识别测试方法的优点是不再需要将所有的方法命名为 testAddition()、testXXX()的形式等等。
例如,下面的方法也可以工作:

 

Junit4使用详解(二)import org.junit.Test;
Junit4使用详解(二)import junit.framework.TestCase;
Junit4使用详解(二)public class AdditionTest  {
Junit4使用详解(二)private int x = 1;
Junit4使用详解(二)private int y = 1;
Junit4使用详解(二)@Test
Junit4使用详解(二)public void addition() {
Junit4使用详解(二)int z = x + y;
Junit4使用详解(二)assertEquals(2, z);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

使用这种方法的好处是:
a)允许我们遵循最适合的应用程序的命名约定。
我们可以将测试方法使用与被测试的类相同的名称(由开发组规范约定)。例如,LoginAction.login() 由 LoginActionTest.login()方法测试、LoginAction.check()由LoginActionTest.check()方法测试等等。
b)使用JUnit4后,测试用例类可以不继承TestCase类,所以我们也就可以扩展被测试类了。
这种方法使得测试受保护的方法非常容易,我们只要将测试用例类继承被测试类,就可以测试受保护方法了。 

@Before(SetUp)

JUnit 3 测试运行程序会在运行每个测试之前自动调用 setUp() 方法。该方法一般会初始化字段、准备数据等等。例如下面的 setUp() 方法,用于设定要加载的路由文件:

 

Junit4使用详解(二)public void setUp()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

在 JUnit4 中,我们仍然可以在每个测试方法运行之前初始化字段或准备数据。然而,完成这些操作的方法不再需要叫做 setUp(),只要用 @Before 注释来指示该方法即可,如下所示:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void initialize()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

JUnit4允许我们使用 @Before 来注释多个方法,这些方法都在每个测试之前运行:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void initialize()  {
Junit4使用详解(二)// 加载此测试用例的servicerouting配置文件
Junit4使用详解(二)ServiceRouting.loadConfig("com/demo/servicerouting.conf");
Junit4使用详解(二)// Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)@Before
Junit4使用详解(二)public void prepareRetData() {
Junit4使用详解(二)//Junit4使用详解(二)
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

 

@After(TearDown)

清除方法与初始化方法类似。在 JUnit3 中,我们要将方法命名为 tearDown() 才可以实现清除方法,但在JUnit4中,只要给方法添加@After标注即可。
例如: 

Junit4使用详解(二)   @After
Junit4使用详解(二)public static void clearContext()  {
Junit4使用详解(二)ActionContext.getContext().put(StrutsStatics.HTTP_REQUEST, null);
Junit4使用详解(二)ActionContext.setContext(null);
Junit4使用详解(二)}
Junit4使用详解(二)


测试方法结束后清除为此测试用例准备的一些数据。
与 @Before 一样,也可以用 @After 来注释多个清除方法,这些方法都在每个测试之后运行。 
最后,我们不再需要显式调用在超类中的初始化和清除方法,只要它们不被覆盖,测试运行程序将根据需要自动为您调用这些方法。
超类中的 @Before 方法在子类中的 @Before 方法之前被调用(这反映了构造函数调用的顺序)。
@After 方法以反方向运行:子类中的方法在超类中的方法之前被调用。否则,多个 @Before 或 @After 方法的相对顺序就得不到保证。

 

 

@Before和@After小结

假设测试类中有如下方法定义:

 

Junit4使用详解(二)@Before
Junit4使用详解(二)public void init() {Junit4使用详解(二)}
Junit4使用详解(二)@After
Junit4使用详解(二)public void destroy(){Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

则Before、After方法的执行流程如图所示:

Junit4使用详解(二)
这种方法有明显的缺陷,如果要初始化的是数据库的链接,或者是一个大的对象的话,而这些资源恰恰是整个测试用例类可以共用的,每次都去申请,确实是种浪费。所以JUnit4引入了@BeforeClass和@AfterClass。

 

@BeforeClass和@AfterClass

JUnit4 也引入了一个 JUnit3 中没有的新特性:类范围的 setUp() 和 tearDown() 方法。任何用 @BeforeClass 注释的方法都将在该类中的测试方法运行之前刚好运行一次,而任何用 @AfterClass 注释的方法都将在该类中的所有测试都运行之后刚好运行一次。
例如,假设类中的每个测试都使用一个数据库连接、一个非常大的数据结构,或者申请其他一些资源。不要在每个测试之前都重新创建它,您可以创建它一次,用完后将其销毁清除。该方法将使得有些测试案例运行起来快得多。
注意:被注释为 BeforeClass和AfterClass 的方法必须为static方法。 
用法如下:

Junit4使用详解(二)    @BeforeClass
Junit4使用详解(二)public static void classInit()  {
Junit4使用详解(二)Map callRet = new HashMap();
Junit4使用详解(二)List<ErrorCodeMessageBean> list = new ArrayList<ErrorCodeMessageBean>();
Junit4使用详解(二)list.add(createMsgBean("TDE0001", "第一个错误消息"));
Junit4使用详解(二)list.add(createMsgBean("TDP9999", "格式化{0}{1}"));
Junit4使用详解(二)list.add(createMsgBean("TDE1000~TDF0001", "区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("TDG0001~", "有下限的区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("~TDD0001", "有上限的区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("~", "默认的消息"));
Junit4使用详解(二)callRet.put(ErrorCodeMessageBean.codeMsgBeanKey, list);
Junit4使用详解(二)ServiceCall.expectLastCallReturn(callRet);
Junit4使用详解(二)}
Junit4使用详解(二)@Test
Junit4使用详解(二)public void oneTestMethod() {
Junit4使用详解(二)//Junit4使用详解(二).
Junit4使用详解(二)}
Junit4使用详解(二)@AfterClass
Junit4使用详解(二)public static void classDestroy() {
Junit4使用详解(二)ServiceCall.expectLastCallReturn(null);
Junit4使用详解(二)}
Junit4使用详解(二)

这个特定虽然很好,但是一定要小心对待这个特性。它有可能会违反测试的独立性,并引入非预期的混乱。如果一个测试在某种程度上改变了 @BeforeClass 所初始化的一个对象,那么它有可能会影响其他测试的结果。也就是说,由BeforeClass申请或创建的资源,如果是整个测试用例类共享的,那么尽量不要让其中任何一个测试方法改变那些共享的资源,这样可能对其他测试方法有影响。它有可能在测试套件中引入顺序依赖,并隐藏 bug。

BeforeClass和AfterClass的执行流程如下:

这种方法有明显的缺陷,如果要初始化的是数据库的链接,或者是一个大的对象的话,而这些资源恰恰是整个测试用例类可以共用的,每次都去申请,确实是种浪费。所以JUnit4引入了@BeforeClass和@AfterClass。

 

@BeforeClass和@AfterClass

JUnit4 也引入了一个 JUnit3 中没有的新特性:类范围的 setUp() 和 tearDown() 方法。任何用 @BeforeClass 注释的方法都将在该类中的测试方法运行之前刚好运行一次,而任何用 @AfterClass 注释的方法都将在该类中的所有测试都运行之后刚好运行一次。
例如,假设类中的每个测试都使用一个数据库连接、一个非常大的数据结构,或者申请其他一些资源。不要在每个测试之前都重新创建它,您可以创建它一次,用完后将其销毁清除。该方法将使得有些测试案例运行起来快得多。
注意:被注释为 BeforeClass和AfterClass 的方法必须为static方法。 
用法如下:

Junit4使用详解(二)   @BeforeClass
Junit4使用详解(二)public static void classInit()  {
Junit4使用详解(二)Map callRet = new HashMap();
Junit4使用详解(二)List<ErrorCodeMessageBean> list = new ArrayList<ErrorCodeMessageBean>();
Junit4使用详解(二)list.add(createMsgBean("TDE0001", "第一个错误消息"));
Junit4使用详解(二)list.add(createMsgBean("TDP9999", "格式化{0}{1}"));
Junit4使用详解(二)list.add(createMsgBean("TDE1000~TDF0001", "区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("TDG0001~", "有下限的区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("~TDD0001", "有上限的区间错误消息"));
Junit4使用详解(二)list.add(createMsgBean("~", "默认的消息"));
Junit4使用详解(二)callRet.put(ErrorCodeMessageBean.codeMsgBeanKey, list);
Junit4使用详解(二)ServiceCall.expectLastCallReturn(callRet);
Junit4使用详解(二)}
Junit4使用详解(二)@Test
Junit4使用详解(二)public void oneTestMethod() {
Junit4使用详解(二)//Junit4使用详解(二).
Junit4使用详解(二)}
Junit4使用详解(二)@AfterClass
Junit4使用详解(二)public static void classDestroy() {
Junit4使用详解(二)ServiceCall.expectLastCallReturn(null);
Junit4使用详解(二)}
Junit4使用详解(二)

这个特定虽然很好,但是一定要小心对待这个特性。它有可能会违反测试的独立性,并引入非预期的混乱。如果一个测试在某种程度上改变了 @BeforeClass 所初始化的一个对象,那么它有可能会影响其他测试的结果。也就是说,由BeforeClass申请或创建的资源,如果是整个测试用例类共享的,那么尽量不要让其中任何一个测试方法改变那些共享的资源,这样可能对其他测试方法有影响。它有可能在测试套件中引入顺序依赖,并隐藏 bug。

BeforeClass和AfterClass的执行流程如下:
Junit4使用详解(二)



测试异常@Test(expected=XXXException.class)

异常测试是 JUnit4 中的最大改进。旧式的异常测试是在抛出异常的代码中放入 try 块,然后在 try 块的末尾加入一个 fail() 语句。
例如,该方法测试被零除抛出一个 ArithmeticException:

 

Junit4使用详解(二)public void testDivisionByZero()  {
Junit4使用详解(二)try {
Junit4使用详解(二)int n = 2 / 0;
Junit4使用详解(二)fail("Divided by zero!");
Junit4使用详解(二)}
Junit4使用详解(二)catch (ArithmeticException success) {
Junit4使用详解(二)assertNotNull(success.getMessage());
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

该方法不仅难看,而且写起来也繁琐。在 JUnit 4 中,我们现在可以编写抛出异常的代码,并使用注释来声明该异常是预期的:

 

Junit4使用详解(二)    @Test(expected = BusinessException.class)
Junit4使用详解(二)public void testExecuteNameEmpty() throws Exception  {
Junit4使用详解(二)BookList bListAction = new BookList();
Junit4使用详解(二)bListAction.setName("");
Junit4使用详解(二)bListAction.execute();
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

附被测试代码(如果输入name为empty,则抛出BusinessException,若name不为"liming",则抛出MessageException异常):

Junit4使用详解(二)    @Override
Junit4使用详解(二)public String execute() throws Exception  {
Junit4使用详解(二)if (StringUtils.isEmpty(name)) {
Junit4使用详解(二)throw new BusinessException("~", "name cant't empty.");
Junit4使用详解(二)}
Junit4使用详解(二)if (!StringUtils.equals("liming", name.trim())) {
Junit4使用详解(二)throw new MessageException(name + " have no limits.");
Junit4使用详解(二)}
Junit4使用详解(二)Map ret = serviceCall.call(JMockService.queryDtlInfo, null);
Junit4使用详解(二)orderId = (String) ret.get("OrderId");
Junit4使用详解(二)dataList = (List) ret.get("Data");
Junit4使用详解(二)return SUCCESS;
Junit4使用详解(二)}
Junit4使用详解(二)

 

参数化测试

为了保证单元测试的严谨性,我们经常要模拟很多种输入参数,来确定我们的功能代码是可以正常工作的,为此我们编写大量的单元测试方法。可是这些测试方法都是大同小异:代码结构都是相同的,不同的仅仅是测试数据和期望输出值。
JUnit4 的参数化测试方法给我们提供了更好的方法,将测试方法中相同的代码结构提取出来,提高代码的重用度,减少复制粘贴代码的痛苦。
例如下面的功能代码(格式化字符串,将驼峰规则的字符串以"_"分隔):

 

Junit4使用详解(二)public class WordDealUtil  {
Junit4使用详解(二)public static String wordFormat4DB(String name) {
Junit4使用详解(二)if (name == null) {
Junit4使用详解(二)return null;
Junit4使用详解(二)}
Junit4使用详解(二)Pattern p = Pattern.compile("[A-Z]");
Junit4使用详解(二)Matcher m = p.matcher(name);
Junit4使用详解(二)StringBuffer sb = new StringBuffer();
Junit4使用详解(二)while (m.find()) {
Junit4使用详解(二)if (m.start() != 0) {
Junit4使用详解(二)m.appendReplacement(sb, ("_" + m.group()).toLowerCase());
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)return m.appendTail(sb).toString().toLowerCase();
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

没有使用参数化的测试用例代码:

Junit4使用详解(二)public class WordDealUtilTest  {
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试 null 时的处理情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBNull() {
Junit4使用详解(二)String target = null;
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertNull(result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试空字符串的处理情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBEmpty() {
Junit4使用详解(二)String target = "";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试当首字母大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBegin() {
Junit4使用详解(二)String target = "EmployeeInfo";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_info", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试当尾字母为大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBEnd() {
Junit4使用详解(二)String target = "employeeInfoA";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_info_a", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试多个相连字母大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBTogether() {
Junit4使用详解(二)String target = "employeeAInfo";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_a_info", result);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)

看以上测试用例代码,结构相似,只是输入值与期望输出不同而已,但我们要拷贝很多代码。
使用参数化的测试用例代码:

Junit4使用详解(二)@SuppressWarnings("unchecked")
Junit4使用详解(二)@RunWith(Parameterized.class)
Junit4使用详解(二)public class WordDealUtilTestWithParam  {
Junit4使用详解(二)private String expected;
Junit4使用详解(二)private String target;
Junit4使用详解(二)@Parameters
Junit4使用详解(二)public static Collection words() {
Junit4使用详解(二)return Arrays.asList(new Object[][] {
Junit4使用详解(二){ "employee_info", "employeeInfo" },  // 测试一般的处理情况
Junit4使用详解(二){ null, null },                         // 测试 null 时的处理情况
Junit4使用详解(二){ "", "" },                             // 测试空字符串时的处理情况
Junit4使用详解(二){ "employee_info", "EmployeeInfo" },    // 测试当首字母大写时的情况
Junit4使用详解(二){ "employee_info_a", "employeeInfoA" }, // 测试当尾字母为大写时的情况
Junit4使用详解(二){ "employee_a_info", "employeeAInfo" }  // 测试多个相连字母大写时的情况
Junit4使用详解(二)});
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 参数化测试必须的构造函数
Junit4使用详解(二)* @param expected     期望的测试结果,对应参数集中的第一个参数
Junit4使用详解(二)* @param target     测试数据,对应参数集中的第二个参数
Junit4使用详解(二)*/
Junit4使用详解(二)public WordDealUtilTestWithParam(String expected, String target) {
Junit4使用详解(二)this.expected = expected;
Junit4使用详解(二)this.target = target;
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试将 Java 对象名称到数据库名称的转换
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DB() {
Junit4使用详解(二)Assert.assertEquals(expected, WordDealUtil.wordFormat4DB(target));
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)

很明显,代码简单且很清晰了。在静态方法 words 中,我们使用二维数组来构建测试所需要的参数列表,其中每个数组中的元素的放置顺序并没有什么要求,只要和构造函数中的顺序保持一致就可以了。现在如果再增加一种测试情况,只需要在静态方法 words 中添加相应的数组即可,不再需要复制粘贴出一个新的方法出来了。
这种参数化的测试用例写法,很适用于一些共用的功能方法。



测试异常@Test(expected=XXXException.class)

异常测试是 JUnit4 中的最大改进。旧式的异常测试是在抛出异常的代码中放入 try 块,然后在 try 块的末尾加入一个 fail() 语句。
例如,该方法测试被零除抛出一个 ArithmeticException:

 

Junit4使用详解(二)public void testDivisionByZero()  {
Junit4使用详解(二)try {
Junit4使用详解(二)int n = 2 / 0;
Junit4使用详解(二)fail("Divided by zero!");
Junit4使用详解(二)}
Junit4使用详解(二)catch (ArithmeticException success) {
Junit4使用详解(二)assertNotNull(success.getMessage());
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

该方法不仅难看,而且写起来也繁琐。在 JUnit 4 中,我们现在可以编写抛出异常的代码,并使用注释来声明该异常是预期的:

Junit4使用详解(二)   @Test(expected = BusinessException.class)
Junit4使用详解(二)public void testExecuteNameEmpty() throws Exception  {
Junit4使用详解(二)BookList bListAction = new BookList();
Junit4使用详解(二)bListAction.setName("");
Junit4使用详解(二)bListAction.execute();
Junit4使用详解(二)}
Junit4使用详解(二)
附被测试代码(如果输入name为empty,则抛出BusinessException,若name不为"liming",则抛出MessageException异常): 
Junit4使用详解(二)    @Override
Junit4使用详解(二)public String execute() throws Exception  {
Junit4使用详解(二)if (StringUtils.isEmpty(name)) {
Junit4使用详解(二)throw new BusinessException("~", "name cant't empty.");
Junit4使用详解(二)}
Junit4使用详解(二)if (!StringUtils.equals("liming", name.trim())) {
Junit4使用详解(二)throw new MessageException(name + " have no limits.");
Junit4使用详解(二)}
Junit4使用详解(二)Map ret = serviceCall.call(JMockService.queryDtlInfo, null);
Junit4使用详解(二)orderId = (String) ret.get("OrderId");
Junit4使用详解(二)dataList = (List) ret.get("Data");
Junit4使用详解(二)return SUCCESS;
Junit4使用详解(二)}
Junit4使用详解(二)

 

参数化测试

为了保证单元测试的严谨性,我们经常要模拟很多种输入参数,来确定我们的功能代码是可以正常工作的,为此我们编写大量的单元测试方法。可是这些测试方法都是大同小异:代码结构都是相同的,不同的仅仅是测试数据和期望输出值。
JUnit4 的参数化测试方法给我们提供了更好的方法,将测试方法中相同的代码结构提取出来,提高代码的重用度,减少复制粘贴代码的痛苦。
例如下面的功能代码(格式化字符串,将驼峰规则的字符串以"_"分隔):

Junit4使用详解(二)public class WordDealUtil  {
Junit4使用详解(二)public static String wordFormat4DB(String name) {
Junit4使用详解(二)if (name == null) {
Junit4使用详解(二)return null;
Junit4使用详解(二)}
Junit4使用详解(二)Pattern p = Pattern.compile("[A-Z]");
Junit4使用详解(二)Matcher m = p.matcher(name);
Junit4使用详解(二)StringBuffer sb = new StringBuffer();
Junit4使用详解(二)while (m.find()) {
Junit4使用详解(二)if (m.start() != 0) {
Junit4使用详解(二)m.appendReplacement(sb, ("_" + m.group()).toLowerCase());
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)return m.appendTail(sb).toString().toLowerCase();
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)没有使用参数化的测试用例代码: 
Junit4使用详解(二)
Junit4使用详解(二)public class WordDealUtilTest {
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试 null 时的处理情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBNull() {
Junit4使用详解(二)String target = null;
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertNull(result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试空字符串的处理情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBEmpty() {
Junit4使用详解(二)String target = "";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试当首字母大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBegin() {
Junit4使用详解(二)String target = "EmployeeInfo";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_info", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试当尾字母为大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBEnd() {
Junit4使用详解(二)String target = "employeeInfoA";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_info_a", result);
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试多个相连字母大写时的情况
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DBTogether() {
Junit4使用详解(二)String target = "employeeAInfo";
Junit4使用详解(二)String result = WordDealUtil.wordFormat4DB(target);
Junit4使用详解(二)assertEquals("employee_a_info", result);
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)
Junit4使用详解(二)

看以上测试用例代码,结构相似,只是输入值与期望输出不同而已,但我们要拷贝很多代码。
使用参数化的测试用例代码:

Junit4使用详解(二)@SuppressWarnings("unchecked")
Junit4使用详解(二)@RunWith(Parameterized.class)
Junit4使用详解(二)public class WordDealUtilTestWithParam  {
Junit4使用详解(二)private String expected;
Junit4使用详解(二)private String target;
Junit4使用详解(二)@Parameters
Junit4使用详解(二)public static Collection words() {
Junit4使用详解(二)return Arrays.asList(new Object[][] {
Junit4使用详解(二){ "employee_info", "employeeInfo" },  // 测试一般的处理情况
Junit4使用详解(二){ null, null },                         // 测试 null 时的处理情况
Junit4使用详解(二){ "", "" },                             // 测试空字符串时的处理情况
Junit4使用详解(二){ "employee_info", "EmployeeInfo" },    // 测试当首字母大写时的情况
Junit4使用详解(二){ "employee_info_a", "employeeInfoA" }, // 测试当尾字母为大写时的情况
Junit4使用详解(二){ "employee_a_info", "employeeAInfo" }  // 测试多个相连字母大写时的情况
Junit4使用详解(二)});
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 参数化测试必须的构造函数
Junit4使用详解(二)* @param expected     期望的测试结果,对应参数集中的第一个参数
Junit4使用详解(二)* @param target     测试数据,对应参数集中的第二个参数
Junit4使用详解(二)*/
Junit4使用详解(二)public WordDealUtilTestWithParam(String expected, String target) {
Junit4使用详解(二)this.expected = expected;
Junit4使用详解(二)this.target = target;
Junit4使用详解(二)}
Junit4使用详解(二)/**
Junit4使用详解(二)* 测试将 Java 对象名称到数据库名称的转换
Junit4使用详解(二)*/
Junit4使用详解(二)@Test
Junit4使用详解(二)public void wordFormat4DB() {
Junit4使用详解(二)Assert.assertEquals(expected, WordDealUtil.wordFormat4DB(target));
Junit4使用详解(二)}
Junit4使用详解(二)}
Junit4使用详解(二)

很明显,代码简单且很清晰了。在静态方法 words 中,我们使用二维数组来构建测试所需要的参数列表,其中每个数组中的元素的放置顺序并没有什么要求,只要和构造函数中的顺序保持一致就可以了。现在如果再增加一种测试情况,只需要在静态方法 words 中添加相应的数组即可,不再需要复制粘贴出一个新的方法出来了。
这种参数化的测试用例写法,很适用于一些共用的功能方法。