Java学习笔记(八)——JUnit单元测试

一、JUnit Test

(一)单元测试的概念

1 . 单元测试是针对最小的功能单元编写测试代码
2 . Java程序最小的功能单元是方法
3 . 单元测试就是针对单个Java方法的测试

(二)测试驱动开发

测试驱动开发TDD:Test-Driven Development
在TDD开发模式下,在编写一个接口之后,便可以立即编写一个测试,之后再编写实现类,之后运行测试。如果测试没有通过,就继续返回并修改代码,直至测试通过,完成整个测试任务。

(三)main方法测试的缺点

1 . 只能存在一个main方法,不能将测试代码分离
2 . 没有打印出测试结果和期望结果

所以就需要一种测试框架来帮助我们编写测试,这就产生了单元测试的概念

(四)单元测试的优点

1 . 可以确保单个方法运行正常
2 . 如果修改了方法代码,只需确保其对应的单元测试通过
3 . 测试代码本身就可以作为示例代码,用来演示如何调用该方法
4 . 可以自动化运行所有测试并获得报告(报告可以统计成功的测试与失败的测试,还可以统计代码覆盖率)

(五)JUnit单元测试框架

1 . JUnit的概念

JUnit是一个开源的Java语言单元测试框架:

  • 专门针对Java语言设计,使用最为广泛
  • JUnit是事实上的标准单元测试框架

2 . JUnit的特点

  • 可以使用断言(Assertion)来测试期望结果
  • 可以方便的组织和运行测试
  • 可有方便的查看结果
  • 常用IDE都集成了JUnit
  • 可以方便的继承到Maven中

3 . JUnit的设计

  • TestCase:一个TestCase代表一个测试
  • TestSuite:一个TestSuite包含一组TestCase,代表一组测试
  • TestFixture:一个TestFixture代表一个测试环境
  • TestResult:用于收集测试结果
  • TestRunner:用于运行测试
  • TestListener:用于监听测试过程,收集测试数据
  • Assert:用于断言测试结果是否正确

4 . 使用Assert断言

在使用Assert断言时,常使用import static org.junit.Assert.*;

  • 断言相等:assertEquals(100, x)
  • 断言数组内容相等:assertArrayEquals({1, 2, 3 }, x)
  • 断言浮点数相等:assertEquals(3.1416, x, 0.0001)(必须设置误差值)
  • 断言为null:assertNull(x)
  • 断言真伪性:assertTrue(x > 0)/assertFalse(x < 0)
  • 其他断言:assertNotEquals/assertNotNull等

(六)编写单元测试

  • 一个TestSuite包含一组相关的测试方法
  • 使用assert断言测试结果(注意浮点数assertEquals要指定delta)
  • 每个测试方法必须完全独立
  • 测试代码必须非常简单
  • 不能为测试代码再编写测试
  • 测试需要覆盖各种输入条件,特别是边界条件

二、使用JUnit

(一)使用Before与After

1 . FixTure

在JUnit中,同一个测试单元内的多个测试方法,在测试前都需要初始化某些对象,并且在测试后可能需要清理资源fileInputStream.close(),也就是Test FixTure(初始化测试资源)
在JUnit中

  • 使用@Before方法初始化测试资源
  • 使用@After方法清理测试资源
public class CalculatorTest {
	Calculator calc;
	@Before
	//初始化测试资源
	public void setUp()  {
		calc = new Calculator();
	}
	@After
	//清理测试资源
	public void tearDown() {
		calc = null;
	}
	@Test
	public void testCalcAdd2Numbers() {
		assertEquals(3, calc.calculate("1+2"));
	}
}

2 . 执行方法

JUnit对于每一个@Test方法:

  • 首先实例化方法
  • 执行@Before方法
  • 执行@Test方法
  • 执行@After方法

所以使用@Before和@After方法可以保证:

  • 单个@Test方法在执行测试前都会创建新的Test实例
  • 实例变量的状态不会传递给下一个@Test方法
  • 单个@Test执行前后便会执行@Before和@After方法

在编写代码时,@Before方法初始化的对象存放在实例字段中,因为实例字段的状态不会影响到下一个@Test

3 . @BeforeClass和@AfterClass静态方法

  • 在执行所有@Test之前,会执行@BeforeClass静态方法
  • 之后进行所有测试
  • 在执行所有@Test之后,执行@AfterClass静态方法
注意:
  • @BeforeClass静态方法初始化的对象只能存放在静态字段中
  • 静态字段的状态会影响到所有@Test

4 . JUnit Fixture

  • @Before:初始化测试对象,例如:Input = new FileInputStream();
  • @After:销毁@Before创建的测试对象,例如:input.close();
  • @BeforeClass:初始化非常耗时的资源,例如:创建数据库
  • @AfterClass:清理@BeforeClass创建的资源,例如:删除数据库

下面是一段测试代码:

import static org.junit.Assert.*;

import org.junit.Test;

public class CalculatorTest {

	@Test
	public void testCalcAdd2Numbers() {
		Calculator calc = new Calculator();
		int r = calc.calculate("1+2");
		assertEquals(3, r);
	}

	@Test
	public void testCalcAdd3Numbers() {
		Calculator calc = new Calculator();
		int r = calc.calculate("1+2+5");
		assertEquals(8, r);
	}

	@Test
	public void testCalcAddLargeNumbers() {
		Calculator calc = new Calculator();
		int r = calc.calculate("123+456");
		assertEquals(579, r);
	}

	@Test
	public void testCalcWithWhiteSpaces() {
		Calculator calc = new Calculator();
		int r = calc.calculate("1 + 5 + 10 ");
		assertEquals(16, r);
	}

}

我们可以将通用部分改写:

package com.feiyangedu.sample;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {

	Calculator calc;
	@Before
	public void setUp() {
		calc = new Calculator();
	}
	
	@Test
	public void testCalcAdd2Numbers() {
		int r = calc.calculate("1+2");
		assertEquals(3, r);
	}

	@Test
	public void testCalcAdd3Numbers() {
		int r = calc.calculate("1+2+5");
		assertEquals(8, r);
	}

	@Test
	public void testCalcAddLargeNumbers() {
		int r = calc.calculate("123+456");
		assertEquals(579, r);
	}

	@Test
	public void testCalcWithWhiteSpaces() {
		int r = calc.calculate("1 + 5 + 10 ");
		assertEquals(16, r);
	}
}

可以看到在上面改写之后的代码中,可以让每一个@Test共享@Before的资源,然后便可以删除每一个@Test中需要初始化的资源,方便进行单元测试和修改代码。

(二)异常测试

在Java中,我们需要对可能抛出的异常进行测试,由于异常本身是方法签名的一部分,所以需要我们测试错误的输入是否会导致特定的异常。

1 . 如何测试异常

在Java中可以使用try…catch语句来捕获异常,可是如果需要捕获的异常较多,就需要我们编写大量的try…catch语句代码。
所以我们测试异常还可以使用@Test(expected=Exception.class)

@Test(expected = NumberFormatException.class)
public void testNumberFormatException() {
	Integer.parseInt(null);
}

在上面这段代码中,我们使用expected来指定异常的class。

  • 在测试代码中,如果抛出了指定类型的异常,则测试通过
  • 如果没有抛出指定类型的异常,或者抛出的异常类型错误,则测试失败
  • 对可能发生的每种类型的异常进行测试

(三)参数化测试

如果待测试的输入和输出是一组数据,可以把测试数据组织起来,用不同的测试数据调用相同的测试方法,这种方法称为参数化测试。

在参数化测试过程中:

  • 参数必须由静态方法date()返回
  • 返回类型必须为Collection<Object[]>
  • 静态方法必须标记为@Pararmeters
  • 测试类必须标记为@RunWith(Parameterized.class)
  • 构造方法参数必须与测试参数相对应

可以通过@Parameter(index)标记public字段,这样就不必去编写构造方法。JUnit会自动创建@Test实例,然后将参数注入到public字段中。

package com.feiyangedu.sample;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
//标记@RunWith
public class CalculatorTest {

	@Parameters
	//标记@Patameters注解
	//定义静态方法data(),返回类型是Collection,返回List类型的数据
	public static Collection<?> data() {
		return Arrays
				.asList(new Object[][] { { "1+2", 3 }, { "1+2+5", 8 }, { "123+456", 579 }, { " 1 + 5 + 10 ", 16 } });
	}

	Calculator calc;

	@Parameter(0)
	public String input;

	@Parameter(1)
	public int expected;

	@Before
	//初始化测试资源
	public void setUp() {
		calc = new Calculator();
	}

	@Test
	//编写测试方法
	public void testCalculate() {
		int r = calc.calculate(this.input);
		assertEquals(this.expected, r);
	}
}

(四)超时测试

在JUnit测试中,可以为单个测试设置超时:
例如:超时设置为1秒:@Test(timeout=1000)(这里的计量单位是毫秒)
注意:超时测试不能取代性能测试和压力测试

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值