MyBatis入坑之Junit

681 阅读8分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

上一篇中的环境搭建比较简陋,我们需要进一步的完善我们的学习环境,我们需要加入更多的框架,来丰富我们的学习,并且我们通过上一遍了解到了两种开发模式,如图所示:

image-20211031190620282.png

说在前面的话: 快速入门掌握第一段就可以了,不用过多的了解,如果想深入了解可以通篇阅读。

1.1 什么是JUnit?

JUnit是Java编程语言的单元测试框架,用于编写和可重复运行的自动化测试。

1.2 JUnit 特点:

  • JUnit 是一个开放的资源框架,用于编写和运行测试。
  • 提供注解来识别测试方法。
  • 提供断言来测试预期结果。
  • JUnit 测试允许你编写代码更快,并能提高质量。
  • JUnit 优雅简洁。没那么复杂,花费时间较少。
  • JUnit测试可以自动运行并且检查自身结果并提供即时反馈。所以也没有必要人工梳理测试结果的报告。
  • JUnit测试可以被组织为测试套件,包含测试用例,甚至其他的测试套件。
  • JUnit在一个条中显示进度。如果运行良好则是绿色;如果运行失败,则变成红色。

1.3 JUnit 注解

注解描述
@Test测试注解,标记一个方法可以作为一个测试用例 。
@BeforeBefore注解表示,该方法必须在类中的每个测试之前执行,以便执行某些必要的先决条件。
@BeforeClassBeforeClass注解指出这是附着在静态方法必须执行一次并在类的所有测试之前,这种情况一般用于测试计算、共享配制方法(如数据库连接)。
@AfterAfter注释表示,该方法在每项测试后执行(如执行每一个测试后重置某些变量,删除临时变量等)。
@AfterClass当需要执行所有测试在JUnit测试用例类后执行,AlterClass注解可以使用以清理一些资源(如数据库连接),注意:方法必须为静态方法。
@Ignore当想暂时禁用特定的测试执行可以使用这个注解,每个被注解为@Ignore的方法将不再执行
@Runwith@Runwith就是放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。
@Parameters用于使用参数化功能。
@SuiteClasses用于套件测试

1.4 JUnit 断言

断言描述
void assertEquals([String message],expected value,actual value)断言两个值相等。值类型可能是int,short,long,byte,char,Object,第一个参数是一个可选字符串消息
void assertTrue([String message],boolean condition)断言一个条件为真
void assertFalse([String message],boolean condition)断言一个条件为假
void assertNotNull([String message],java.lang.Object object)断言一个对象不为空(null)
void assertNull([String message],java.lang.Object object)断言一个对象为空(null)
void assertSame([String message],java.lang.Object expected,java.lang.Object actual)断言两个对象引用相同的对象
void assertNotSame([String message],java.lang.Object unexpected,java.lang.Object actual)断言两个对象不是引用同一个对象
void assertArrayEquals([String message],expectedArray,resultArray)断言预期数组和结果数组相等,数组类型可能是int,short,long,byte,char,Object

1.5 单元(方法)测试框架

不需要使用核心配置文件,直接使用提供的注解即可

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.2</version>
  <scope>test</scope>
</dependency>
package com.hanpang.service;

public class UserService {
    public int sum(int i,int j){
        return i+j;
    }
    public int sub(int i,int j){
        return i-j;
    }
}

package com.hanpang.test;

import com.hanpang.service.UserService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class UserServiceTest {

    private UserService userService ;
    @Before
    public void init(){
        System.out.println("实例化");
        userService = new UserService();
    }
    @After
    public void end(){

    }

    //1.定义的没有返回值并且必须是public
    //2.每个方法的上面设置Test注解
    //3.可以理解为多个main方法
    //4.单元测试:实际结果和预期结果一致,才是正确
    @Test
    public void sumMethodTest(){
        int result = userService.sum(10,20);
        Assert.assertEquals(result,30);
    }
    @Test
    public void subMethodTest(){
        int result = userService.sub(10,20);
        Assert.assertEquals(result,-10);
    }
}

public class AssertionTest {
    @Test
    public void test() {
        String obj1 = "junit";
        String obj2 = "junit";
        String obj3 = "test";
        String obj4 = "test";
        String obj5 = null;
        
        int var1 = 1;
        int var2 = 2;

        int[] array1 = {1, 2, 3};
        int[] array2 = {1, 2, 3};

        Assert.assertEquals(obj1, obj2);

        Assert.assertSame(obj3, obj4);
        Assert.assertNotSame(obj2, obj4);
        
        Assert.assertNotNull(obj1);
        Assert.assertNull(obj5);

        Assert.assertTrue(var1 < var2);
        Assert.assertFalse(var1 > var2);

        Assert.assertArrayEquals(array1, array2);

    }
}

在以上类中我们可以看到,这些断言方法是可以工作的。

  • assertEquals() 如果比较的两个对象是相等的,此方法将正常返回;否则失败显示在JUnit的窗口测试将中止。
  • assertSame() 和 assertNotSame() 方法测试两个对象引用指向完全相同的对象。
  • assertNull() 和 assertNotNull() 方法测试一个变量是否为空或不为空(null)。
  • assertTrue() 和 assertFalse() 方法测试if条件或变量是true还是false。
  • assertArrayEquals() 将比较两个数组,如果它们相等,则该方法将继续进行不会发出错误。否则失败将显示在JUnit窗口和中止测试。

1.6 时间测试

JUnit提供了一个暂停的方便选项,如果一个测试用例比起指定的毫秒数花费了更多的时间,那么JUnit将自动将它标记为失败,timeout参数和@Test注解一起使用,例如@Test(timeout=1000)。 继续使用刚才的例子,现在将testCase1的执行时间延长到5000毫秒,并加上时间参数,设置超时为1000毫秒,然后执行测试类

@Test(timeout = 1000)
public void testCase1() throws InterruptedException {
    TimeUnit.SECONDS.sleep(5000);
    System.out.println("in test case 1");
}

testCase1被标记为失败,并且抛出异常,执行结果:

org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds

1.7 异常测试

Junit 用代码处理提供了一个追踪异常的选项。你可以测试代码是否它抛出了想要得到的异常。expected 参数和 @Test 注释一起使用。现在让我们看看 @Test(expected)。新建测试方法testCase3()。

@Test(expected = ArithmeticException.class)
public void testCase3() {
    System.out.println("in test case 3");
    int a = 0;
    int b = 1 / a;
}

1.8 参数化测试

Junit 4 引入了一个新的功能参数化测试。参数化测试允许开发人员使用不同的值反复运行同 一个测试。你将遵循 5 个步骤来创建参数化测试:

-为准备使用参数化测试的测试类指定特殊的运行器 org.junit.runners.Parameterized。

  • 为测试类声明几个变量,分别用于存放期望值和测试所用数据。
  • 为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值。
  • 为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的,返回值为 java.util.Collection 的公共静态方法,并在此方法中初始化所有需要测试的参数对。
  • 编写测试方法,使用定义的变量作为参数进行测试。

(1) 什么是@RunWith?

首先要分清几个概念:测试方法、测试类、测试集、测试运行器。

  • 其中测试方法就是用@Test注解的一些函数。
  • 测试类是包含一个或多个测试方法的一个Test.java文件
  • 测试集是一个suite,可能包含多个测试类。
  • 测试运行器则决定了用什么方式偏好去运行这些测试集/类/方法。

而@Runwith就是放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。常见的运行器有:

  • @RunWith(Parameterized.class) 参数化运行器,配合@Parameters使用JUnit的参数化功能
  • @RunWith(Suite.class) @SuiteClasses({ATest.class,BTest.class,CTest.class}) 测试集运行器配合使用测试集功能
  • @RunWith(JUnit4.class), junit4的默认运行器
  • @RunWith(JUnit38ClassRunner.class),用于兼容junit3.8的运行器
  • 一些其它运行器具备更多功能。例如@RunWith(SpringJUnit4ClassRunner.class)集成了spring的一些功能

PrimeNumberCheckerTest.java

/**
 * 步骤一: 指定定参数运行器
 */
@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {

    /**
     * 步骤二:声明变量
     */
    private Integer inputNumber;
    private Boolean expectedResult;
    private PrimeNumberChecker primeNumberChecker;

    /**
     * 步骤三:为测试类声明一个带有参数的公共构造函数,为变量赋值
     */
    public PrimeNumberCheckerTest(Integer inputNumber,
                                  Boolean expectedResult) {
        this.inputNumber = inputNumber;
        this.expectedResult = expectedResult;
    }

    /**
     * 步骤四:为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的,返回值为
     * java.util.Collection 的公共静态方法,并在此方法中初始化所有需要测试的参数对
     *   1)该方法必须由Parameters注解修饰 
          2)该方法必须为public static的 
          3)该方法必须返回Collection类型 
          4)该方法的名字不做要求 
          5)该方法没有参数 
     */
    @Parameterized.Parameters
    public static Collection primeNumbers() {
        return Arrays.asList(new Object[][]{
                {2, true},
                {6, false},
                {19, true},
                {22, false},
                {23, true}
        });
    }

    @Before
    public void initialize() {
        primeNumberChecker = new PrimeNumberChecker();
    }

    /**
     * 步骤五:编写测试方法,使用自定义变量进行测试
     */
    @Test
    public void testPrimeNumberChecker() {
        System.out.println("Parameterized Number is : " + inputNumber);
        Assert.assertEquals(expectedResult,
                primeNumberChecker.validate(inputNumber));
    }
}

PrimeNumberChecker.java

public class PrimeNumberChecker {

    public Boolean validate(final Integer parimeNumber) {
        for (int i = 2; i < (parimeNumber / 2); i++) {
            if (parimeNumber % i == 0) {
                return false;
            }
        }
        return true;
    }
}


1.9 套件测试

“套件测试”是指捆绑了几个单元测试用例并运行起来。在JUnit中,@RunWith 和 @Suite 这两个注解是用来运行套件测试。先来创建几个测试类

ublic class JunitTest1 {

    @Test
    public void printMessage(){
        System.out.println("in JunitTest1");
    }
}
复制代码
public class JunitTest2 {

    @Test
    public void printMessage(){
        System.out.println("in JunitTest2");
    }
}
复制代码
@RunWith(Suite.class)
@Suite.SuiteClasses({
        /**
         * 此处类的配置顺序会影响执行顺序
         */
        JunitTest1.class,
        JunitTest2.class
})
public class JunitSuite {

}
复制代码

执行JunitSuite测试类,执行结果:

in JunitTest1
in JunitTest2
复制代码