JUnit单元测试的基本使用
我们使用Intellij IDEA15来完成日常开发工作,使用JUnit来进行单元测试的工作,对我们所写的代码进行正确性的测试,并为后期的项目维护做好准备,以及提升代码的质量和出错率,Debug的过程一定是痛苦的。
环境搭建以及前期准备
- 首先,在一个新建的项目中,在项目根目录下新建一个测试文件夹,此处为了易于辨识,我们取做“tests”;
- 然后,“Command+;”进入到项目结构,选择Modules子选项并把tests文件夹添加为测试目录,此时文件夹的颜色由灰色变为蓝色,点击ok确认即可;
最后,我们在src目录下的com.sh2zqp.bean包下新建一个类Person,类里面添加一个name属性和一个getName()和一个setName()方法。
public class Person {
private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
注意
之所以把测试代码和源代码分开是因为一般测试代码是不会公开的,只是我们开发人员自己使用,把它们单独放在tests目录下更易于实现这样的管理。
生成测试类PersonTest
- 首先把鼠标定位到Person类上,快捷键Alt(option)+Enter(return),选择Create Tests选项;
- 然后,进入Create Tests设置页面,选择Testing Libary为JUnit4,我们发现里面有不少的测试库可以使用;
- 但是,我们发现JUnit4库并没有被引入项目Module中,此时我们点击fix,选择第一项;,点击ok;
- 最后,我们选择Person类中的getName()方法进行测试,点击ok即可,之后我们发现项目的结构如下。
可以发现测试类PersonTest中的有一个getName的测试方法,方法上面有一个@Test注解
编写测试代码
@Test
public void getName() throws Exception {
Person person = new Person("sh2zqp");
assertEquals("sh2zqp", person.getName());
}
代码说明
- 测试方法上面必须使用@Test注解进行修饰;
- 测试方法必须使用public void 进行修饰,不能带有任何参数;
- 测试方法必须抛出throws Exception异常;
- 新建一个源代码目录tests用来存放测试代码;
- 测试类的包应该与被测试类的包保持一致;
- 测试单元中的每一个方法必须独立测试,每个测试方法之间不能有依赖;
- 测试类使用Test做为类名的后缀(非必要);
- 测试方法使用test作为方法名的前缀(非必要);
运行测试代码
我们也可以把鼠标方法测试类PersonTest或测试方法getName()上,通过快捷键Alt+Enter来完成测试运行。
测试结果如下:
此外,我们通过在页面任意空白处使用Command+N添加新的测试方法,如下:
测试错误示范
我们为setName()方法添加了测试方法
@Test
public void setName() throws Exception {
Person person = new Person("sh2zqp");
person.setName("sh2zqp1");
assertEquals("sh2zqp", person.getName());
}
很明显assertEquals(“sh2zqp”, person.getName())中“sh2zqp”并不是person.getName()所期望的值,因为我们已经通过setName()方法更改为“sh2zqp1”。
另外,setName和getName方法混合使用,说明此处的setName已经不是独立的测试单元。
注意
- Failure 一般是单元测试使用的断言方法判断失败引起,说明预期结果和程序运行结果不一致;
- error 是有代码异常引起的,他产生于测试代码本身中的Bug;
- 测试用例不是用来证明你是对的,而是用来证明你没有错;
JUnit单元测试的深入学习
上一部分我们简单的使用了JUnit进行了单元测试,本部分我们进一步来学习。
JUnit测试流程
在PersonTest测试类中加入如下代码:
@BeforeClass
public static void setUpBeforeClass() throws Exception {
System.out.println("BeforeClass");
}
@AfterClass
public static void setUpAfterClass() throws Exception {
System.out.println("AfterClass");
}
@Before
public void setUp() throws Exception {
System.out.println("Before");
}
@After
public void tearDown() throws Exception {
System.out.println("After");
}
@Test
public void getName() throws Exception {
System.out.println("test getName()");
Person person = new Person("sh2zqp1");
assertEquals("sh2zqp1", person.getName());
}
@Test
public void setName() throws Exception {
System.out.println("test setName()");
Person person = new Person("sh2zqp");
person.setName("sh2zqp1");
assertEquals("sh2zqp1", person.getName());
}
这里我们学习四个新的注解@BeforeClass,@AfterClass,@Before,@After,下面对这几个注解做出说明:
- @BeforeClass:所修饰的方法在所有方法加载前执行,而且他是静态的在类加载后就会执行该方法,在内存中只有一份实例,适合用来加载配置文件;
- @AfterClass:所修饰的方法在所有方法执行完毕之后执行,通常用来进行资源清理,例如关闭数据库连接;
- @Before和@After在每个测试方法执行前都会执行一次。
JUnit常用注解
- @Test:将一个普通的方法修饰成为一个测试方法
- @Test(exception=xxx.class)在运行时忽略某个异常
- @Test(timeout=毫秒数)允许程序运行的时间
@Test可以附带参数,首先在Person类中新增一个divideByPerson()方法,然后在PersonTest类中新增测试方法,具体如下:
public int divideByPerson(int a, int b) {
return a/b;
}
@Test
public void divideByPerson() throws Exception {
assertEquals(6, person.divideByPerson(6,0));
}
测试结果如下:
可以看到ArithmeticException算数错误,报了使用@Test(exception=xxx.class),代码如下:
@Test(expected = ArithmeticException.class)
public void divideByPerson() throws Exception {
assertEquals(6, person.divideByPerson(6,0));
}
运行结果如下:
@Test另外还有一个参数@Test(timeout=毫秒数),其目的给出一个程序运行的最大时间,时间到了会自动停止,一般用于循环执行的代码块,避免出现死循环。
@BeforeClass,@AfterClass,@Before,@After(参见上面的解释)
@Ignore:所修饰的方法会被测试器忽略,里面可以附加一些提示信息@Ignore(“……”)
@Ignore("ignore") @Test public void getName() throws Exception { System.out.println("test getName()"); assertEquals("sh2zqp", person.getName()); }
- @RunWith:更改测试运行器,自定义测试器,需要继承org.junit.runner.Runner,大多数情况下使用默认的测试运行器即可。
- assert断言的使用
静态导入
import static org.junit.Assert
JUnit测试套件的使用
测试套件是组织测试类一起运行的测试类,是随着项目规模的变大的一种简易测试方法,可以批量运行测试类,把这些测试类集中到一个测试套件中即可。
我们新建的一个测试套件类SuiteTest和三个测试类Test1,Test2,Test3,如下:
public class Test1 {
@Test
public void test() throws Exception {
System.out.println("test1......");
}
}
public class Test2 {
@Test
public void test() throws Exception {
System.out.println("test2......");
}
}
public class Test3 {
@Test
public void test() throws Exception {
System.out.println("test3......");
}
}
@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class, Test2.class, Test3.class})
public class SuiteTest {
}
运行结果
说明
- 作为测试套件的入口类,类中不能包含任何方法,并用public修饰;
- 更改测试运行器Suite.class(@RunWith(Suite.class));
- 将需要运行的测试类放入Suite.SuiteClasses({})的数组中,当然里面也可以放测试套件类。
JUnit参数化设置
需要测试的仅仅是测试数据,代码结构是不变的,只需要更改测试数据。
@RunWith(Parameterized.class)
public class ParameterTest {
String expected = "";
String input = "";
@Parameterized.Parameters
public static Collection<Object[]> t() {
return Arrays.asList(new Object[][] {
{"sh2zqp1","sh2zqp1"},
{"sh2zqp2","sh2zqp2"}
});
}
public ParameterTest(String expected, String input) {
this.expected = expected;
this.input = input;
}
@Test
public void testGetName() throws Exception {
Person person = new Person(input);
assertEquals(expected, person.getName());
}
}
具体步骤
- 更改默认的测试运行器为@RunWith(Parameterized.class);
- 声明变量来存放预期值和测试值;
- 声明一个返回值为Collection的公共静态方法,并用@Parameters修饰;
- 为测试类声明一个带有参数的公共构造函数,并在其中为他声明变量赋值。
运行结果
JUnit4在Web项目中的使用
后期添加
参考资料
JUnit 4 with IntelliJ: A quick introduction
JUnit单元测试–IntelliJ IDEA
JUnit—Java单元测试必备工具