单元测试Junit4
1、单元测试入门
所谓单元测试,就是针对最小的功能单元,编写测试代码对其进行正确性测试。
我们想想,咱们之前是怎么进行测试的呢?
比如说我们写了一个学生管理系统,有添加学生、修改学生、删除学生、查询学生等这些功能。要对这些功能这几个功能进行测试,我们是在main方法中编写代码来测试的。
但是在main方法中写测试代码有如下的几个问题,如下图所示:
为了测试更加方便,有一些第三方的公司或者组织提供了很好用的测试框架,给开发者使用。这里介绍一种Junit测试框架。
Junit是第三方公司开源出来的,用于对代码进行单元测试的工具(IDEA已经集成了junit框架)。相比于在main方法中测试有如下几个优点。
在写之前还有几个注意事项:
2、反射实现执行注解的方法
1. 测试方法不能有返回值,只能是void
2. 参数列表不能有参数
3. 只能是public修饰
在上一个笔记中的记录了注解和反射,当时也通过反射的技术获取到了注解中的所有的值,其实我们已经可以通过反射的技术获取到所有使用每个注解的方法,然后在调用其他方法。就比如被@Test注解修饰的方法,我们就可以通过下面的代码来获取到所有的方法,而且我们也会发现,这些方法只能是无参数,无返回值只能是public修饰,这样就更简单了。有如下的测试方法类,我们通过反射的手段来执行这个方法。
public class TestUtils {
@Test
public void testGetSum(){
System.out.println("测试getSum");
}
@Test
public void testMinus(){
System.out.println("测试minus");
}
@Test
public void testMultiply(){
System.out.println("测试multiply");
}
@Test
public void testBy(){
System.out.println("测试by");
}
}
通过反射执行所有的方法
public class MyReflect {
public static void main(String[] args) throws Exception {
// 获取字节码对象
Class<TestUtils> testUtilsClass = TestUtils.class;
// 获取所有的方法
Method[] declaredMethods = testUtilsClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// 判断是否有@Test注解
Test annotation = declaredMethod.getAnnotation(Test.class);
if (annotation!=null){
// 传入一个对象,没有参数,所以不用传
declaredMethod.invoke(testUtilsClass.newInstance());
}
}
}
}
结果如下:反射是不是很香。
测试getSum
测试minus
测试multiply
测试by
另外还有一些默认的规定找的网上的截图:主要就是测试的类名,比如Student类中的有一个方法study 对应的测试类和方法是
StudentTest testStudy
由于Junit是第三方提供的,所以我们需要把jar包导入到我们的项目中,才能使用,具体步骤如下图所示:
先准备一个类,假设写了一个StringUtil工具类,代码如下
public class StringUtil{
public static void printNumber(String name){
System.out.println("名字长度:"+name.length());
}
}
接下来,写一个测试类,测试StringUtil工具类中的方法能否正常使用。
public class StringUtilTest{
@Test
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}
}
写完代码之后,我们会发现测试方法左边,会有一个绿色的三角形按钮。点击这个按钮,就可以运行测试方法。
3、单元测试断言
接下来,我们学习一个单元测试的断言机制。所谓断言:意思是程序员可以预测程序的运行结果,检查程序的运行结果是否与预期一致。
我们在StringUtil类中新增一个测试方法
public static int getMaxIndex(String data){
if(data == null){
return -1;
}
return data.length();
}
接下来,我们在StringUtilTest类中写一个测试方法
public class StringUtilTest{
@Test
public void testGetMaxIndex(){
int index1 = StringUtil.getMaxIndex(null);
System.out.println(index1);
int index2 = StringUtil.getMaxIndex("admin");
System.out.println(index2);
//断言机制:预测index2的结果
Assert.assertEquals("方法内部有Bug",4,index2);
}
}
运行测试方法,结果如下图所示,表示我们预期值与实际值不一致
4、Junit框架的常用注解
除了@Test注解,还有一些其他的注解,我们要知道其他注解标记的方法什么时候执行,以及其他注解在什么场景下可以使用。
注解 | 说明 |
---|---|
@Test | 测试方法 |
@Before | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 |
@AfterClass | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 |
接下来,我们演示一下其他注解的使用。我们在StringUtilTest测试类中,再新增几个测试方法。代码如下
public class StringUtilTest{
@Before
public void test1(){
System.out.println("--> test1 Before 执行了");
}
@BeforeClass
public static void test11(){
System.out.println("--> test11 BeforeClass 执行了");
}
@After
public void test2(){
System.out.println("--> test2 After 执行了");
}
@AfterClass
public static void test22(){
System.out.println("--> test22 AfterClass 执行了");
}
@Test
public void testMethod(){
System.out.println("hello");
}
}
结果如下:
--> test11 BeforeClass 执行了
--> test1 Before 执行了
hello
--> test2 After 执行了
--> test22 AfterClass 执行了
我们现在已经知道每一个注解的作用了,那他们有什么用呢?应用场景在哪里?
我们来看一个例子,假设我想在每个测试方法中使用Socket对象,并且用完之后,需要把Socket关闭。代码就可以按照下面的结构来设计
public class StringUtilTest{
private static Socket socket;
@Before
public void test1(){
System.out.println("--> test1 Before 执行了");
}
@BeforeClass
public static void test11(){
System.out.println("--> test11 BeforeClass 执行了");
//初始化Socket对象
socket = new Socket();
}
@After
public void test2(){
System.out.println("--> test2 After 执行了");
}
@AfterClass
public static void test22(){
System.out.println("--> test22 AfterClass 执行了");
//关闭Socket
socket.close();
}
@Test
public void testMethod(){
System.out.println("hello");
}
}
前面的注解是基于Junit4版本的,再Junit5版本中对注解作了更新,但是作用是一样的。下面就不做演示了
注解 | 说明 |
---|---|
@Test | 测试方法 |
@BeforeEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@AfterEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeAll | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 |
@AfterAll | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 |