1. 程式人生 > >單元測試框架TestNG、Mockito、Unitils-Spring及示例

單元測試框架TestNG、Mockito、Unitils-Spring及示例

一. TestNG  對Junit的擴充套件可通過xml指令碼同時執行多個case
TestNG執行時將經過一下幾個階段:
1. 類級初始化資源處理        @BeforeClass註解標記
2. 方法級別初始化資源處理    @BeforeMethod
3. 執行測試用例中的方法      @Test
4. 方法級別的銷燬          @AfterMethod
5. 類級別的銷燬           @AfterClass
如果定義了多個初始化方法,先執行定義在最後面的,而銷燬方法先執行定義在最前面的。
如果定義多個@Test的用例,會為每個@Test方法生成一個測試用例例項分別執行。
@BeforeClas @AfterClass在一個測試用例例項中只執行一次。

測試方法: 通過@Test標註方法執行一個測試邏輯,然後通過Assert進行斷言。
詳細例項請見:com.test.TestNGLearn

二. Mockito
Mockito只適用於對Service層的方法進行單元測試
Mockito可以mock某些類和方法的執行,設定某些期望的行為和返回值
常用來測試判定邏輯等,通過mock避免執行某些依賴的邏輯

Mockito + Assert 可以方便的測試服務類的邏輯結構。

詳細例項請見:com.test.MockitoLearn

三. Unitils
專用於測試框架整合, 通過配置檔案開啟模組功能。unitils定義了Module介面,通過實現這個介面整合其他框架,同時也可以自定義。
1. ReflectionAssert反射斷言 詳細例項見: com.test.UnitilsLearn
2. 整合Spring               詳細例項見:com.test.UnitilsSpring
3. 整合Hibernate
4. 整合DBUtil
5. 對web層測試

TestNG示例:

package com.test;


import org.testng.annotations.*;

// 用於指定類屬於的分組
// @Test(groups = {"group1"})
public class TestNGLearn {

    @BeforeClass
    public static void init(){
        System.out.println("類初始化");
    }

    @AfterClass
    public static void destroy(){
        System.out.println("類銷燬");
    }

    @BeforeMethod
    public void initMethod(){
        System.out.println("用例執行初始化");
    }

    @Test
    public void testTestNG(){
        System.out.println("一個簡單的測試");
    }

    // 開啟異常測試,如果沒有丟擲指定的異常則測試失敗。
    @Test(enabled = true , expectedExceptions = RuntimeException.class)
    public void testException(){
        throw new RuntimeException();
    }

    // 如果在timeOut指定的毫秒時間內未完成則測試失敗
    @Test(enabled = true, timeOut = 2000)
    public void testTimeOut() throws InterruptedException{
        Thread.sleep(2000);
    }

    // 引數化測試,直接提供批量的測試引數,不必為每個引數寫測試用例
    @DataProvider(name = "testParam")
    public static Object[][] getParameters(){
        String [][] params = {{"第一組第一個引數","第一組第二個引數"},{"第二組第一個引數","第二組第二個引數"}};
        return params;
    }
    // 通過一個二維陣列,指定多組引數,每組可指定多個引數。
    @Test(dataProvider = "testParam")
    public void testBatchParam(String first , String second){
           System.out.println(first + " " +second);
    }

    // 分組測試, 每個測試方法單獨生成例項,並都執行@BeforeMethod,而@BeforeClass之執行一次
    @Test(groups = {"group1"})
    public void testGroup2(){
        System.out.println("分組測試 group1分組的第1個測試用例");
    }

    @Test(groups = {"group1","group2"})
    public void testGroup1_1(){
        System.out.println("分組測試 group2分組的第1個測試用例");
    }

    @Test(groups = {"group2"})
    public void testGroup1_2(){
        System.out.println("分組測試 group2分組的第2個測試用例");
    }


    // 依賴測試
    @Test
    public void testDepond(){
        System.out.println("這是一個被依賴的方法");
    }

    @Test(dependsOnMethods = "testDepond")
    public void testDepond2(){
        System.out.println("前面一個方法執行成功,我才執行");
    }

    @AfterMethod
    public void destroyMethod(){
        System.out.println("用例執行收尾");
    }
}

TestNG指令碼方式執行: 直接右鍵這個xml檔案執行

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<!--配置只要指定某個包,某個類,某個方法,某個組即可執行-->

<!--定義一個測試套件 指定指令碼名稱以進行套件區分-->
<suite name="test_suite_1">
    <!--定義一個測試用例,suite指令碼會執行每個測試用例-->
    <test name="group1_test" >
        <!--分組測試-->
        <groups>
            <!--群組: 將註解指定的分組,再次進行組合-->
            <define name="all">
                <include name="group1"/>
                <include name="gourp2"/>
            </define>


            <!--指定執行的組和不執行的組-->
            <run>
                <include name = "group2" />
            </run>
        </groups>

        <!--選擇一個包中的全部@Test方法-->
        <packages>
            <package name = "com.test">
            </package>
        </packages>
    </test>

    <!--第二個測試用例-->
    <test name="group2_test">

        <!--執行指定某個類中的指定方法-->
        <classes>
            <class name="com.test.TestNGLearn">
                <methods>
                    <include name="testBatchParam"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>

Mokito示例:

package com.test;

import com.learn.aop.business.SayHello;
import com.learn.aop.business.SayHi;
import com.learn.mvc.domains.User;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static org.mockito.Mockito.*;

/**
 * mokito只適用於對Service層的方法進行單元測試
 * mokito可以mock某些類和方法的執行,設定某些期望的行為和返回值
 * 常用來測試判定邏輯等,通過mock避免執行某些依賴的邏輯
 *
 * Mockito + Assert 可以方便的測試服務類的邏輯結構。
 */
public class MockitoLearn {

    private static User user;
    // 註解mock
    @Mock
    SayHi sayHi;

    private SayHello sayHello;

    @BeforeClass
    public void intiClass(){
        // 開啟註解
        MockitoAnnotations.initMocks(this);
    }

    @BeforeMethod
    public void initUser(){
        User user =new User();
        user.setId(3);
        user.setAge(18);
        user.setName("asdfe");
        user.setSex("female");
    }
    // 第一種mock方法
    @Test
    public void testMokito(){
        // 呼叫方法mock產生一個虛擬的服務類
        sayHello=mock(SayHello.class);

        User user=new User();
        doReturn(user).when(sayHello.myUser());

        Assert.assertNotNull(user);
    }

    // 第二中mock方法
    @Test
    public void testMokito2(){

        when(sayHi.getUser()).thenReturn(user);
        User user = sayHi.getUser();

        Assert.assertNotNull(user);
        System.out.println(user.getName());
    }

    // mockito的互動驗證。
    // mockito能記錄某個方法的執行次數。
    @Test
    public void testMockito3(){
        // 註解生成的sayHi mock類
        when(sayHi.getUser()).thenReturn(user);
        // 程式碼生成的sayHello mock類
        sayHello=mock(SayHello.class);

        // 驗證其是否執行
        verify(sayHi).getUser();

        // 驗證某個類某個方法的執行次數
        verify(sayHi,atLeast(1)).getUser();

        verify(sayHello,atLeastOnce()).myUser();
        verify(sayHello,atMost(1)).myUser();
    }
}

Unitils 反射斷言

package com.test;

import com.learn.mvc.domains.User;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.unitils.reflectionassert.ReflectionAssert;
import org.unitils.reflectionassert.ReflectionComparatorMode;

// 測試反射斷言
public class UnitilsLearn {
    private static User user1;
    private static User user2;
    private static User user3;

    @BeforeMethod
    public void initUser() {
        user1 = new User();
        user1.setName("vvvasd");
        user2 = new User();
        user3 = new User();
    }

    // 反射斷言,判斷兩個例項是否相等,此處應該不相等,所以測試不通過。
    @Test
    public void testReflection() {
        ReflectionAssert.assertReflectionEquals(user1, user3);

    }

    // 反射斷言,判斷兩個例項是否相等
    @Test
    public void testReflection2() {
        ReflectionAssert.assertReflectionEquals(user3, user2);

    }

    // 集合的比較 三種類型: 忽略順序,忽略預設值,判斷是否有值
    @Test
    public void testReflection3() {
        Integer orderList1[] = new Integer[]{1, 2, 3};
        Integer orderList2[] = new Integer[]{3, 2, 1};

        // 忽略順序
        ReflectionAssert.assertReflectionEquals(orderList1,orderList1, ReflectionComparatorMode.LENIENT_ORDER);
        // 忽略預設值
        ReflectionAssert.assertReflectionEquals(orderList1,orderList1, ReflectionComparatorMode.IGNORE_DEFAULTS);
        // 對日期型別只判斷是否有值
        ReflectionAssert.assertReflectionEquals(orderList1,orderList1, ReflectionComparatorMode.LENIENT_DATES);
    }

    // 斷言一個POJO類中的屬性是否為某個值
    @Test
    public void testPropertyEqual(){
        User user =new User();
        user.setName("hello");
        // Lenient模式忽略順序和預設值。
        ReflectionAssert.assertPropertyLenientEquals("name","hello",user);
    }
}

Unitils 整合spring   注意:unitils預設的啟動配置中,springModule必須在databaseModule之後啟動,所以如果沒有配置,database資料來源將導致測試執行失敗。可以自定義一個unitils.property檔案配置啟動方式

自定義配置檔案如下: 直接複製了預設的配置檔案:修改

unitils.module.spring.className=org.unitils.spring.SpringModule
unitils.module.spring.runAfter=database
unitils.module.spring.enabled=true

對於unitils.module.spring.runAfter屬性,將database去掉變為:unitils.module.spring.runAfter=   


unitils.modules=database,dbunit,hibernate,mock,easymock,inject,spring,jpa,io

#### Unitils core configuration ###
# For each module, the implementation class is listed in unitils.module.<modulename>.className, the sequence of the
# execution of their code is influenced by unitils.module.<modulename>.runAfter. Disabling a module can be performed by
# setting unitils.module.<modulename>.enabled to false.
unitils.module.database.className=org.unitils.database.DatabaseModule
unitils.module.database.runAfter=
unitils.module.database.enabled=false

unitils.module.hibernate.className=org.unitils.orm.hibernate.HibernateModule
unitils.module.hibernate.runAfter=
unitils.module.hibernate.enabled=false

unitils.module.dbunit.className=org.unitils.dbunit.DbUnitModule
unitils.module.dbunit.runAfter=
unitils.module.dbunit.enabled=false

unitils.module.mock.className=org.unitils.mock.MockModule
unitils.module.mock.runAfter=
unitils.module.mock.enabled=false

unitils.module.easymock.className=org.unitils.easymock.EasyMockModule
unitils.module.easymock.runAfter=
unitils.module.easymock.enabled=false

unitils.module.inject.className=org.unitils.inject.InjectModule
unitils.module.inject.runAfter=
unitils.module.inject.enabled=false

unitils.module.spring.className=org.unitils.spring.SpringModule
unitils.module.spring.runAfter=
unitils.module.spring.enabled=true

unitils.module.jpa.className=org.unitils.orm.jpa.JpaModule
unitils.module.jpa.runAfter=
unitils.module.jpa.enabled=false

unitils.module.io.className=org.unitils.io.IOModule
unitils.module.io.runAfter=
unitils.module.io.enabled=false
package com.test;

import com.learn.aop.business.SayHello;
import org.junit.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.unitils.UnitilsTestNG;
import org.unitils.spring.annotation.SpringApplicationContext;
import org.unitils.spring.annotation.SpringBean;
import org.unitils.spring.annotation.SpringBeanByType;

/**
 * 注意一定要整合UnitilsTestNG類
 * unitils 啟動會開啟springModule然後初始化spring容器。
 * Unitils TestNG一定要一個數據源才能啟動
 */

@SpringApplicationContext({"spring-servlet.xml"})
public class UnitilsSpring extends UnitilsTestNG {


    @SpringBean("sayHelloTarget")
    public SayHello sayHello;


//    @SpringBeanByName
//    public SayHello sayHello2;

    @SpringBeanByType
    public SayHello sayHello3;

    @BeforeMethod
    public void before() {
        System.out.println("開始準備");
    }

    @AfterMethod
    public void after() {
        System.out.println("執行結束");
    }

    // 測試手動啟動
//    @Test
//    public void testSpring() {
//        ApplicationContext context = new ClassPathXmlApplicationContext("spring-test.xml");
//        sayHello2 = (SayHello) context.getBean("sayHello");
//        Assert.assertNotNull(sayHello2);
//    }

    // 測試註解啟動
    @Test
    public void testSpring2() {
        sayHello.sayHello();
        Assert.assertNotNull(sayHello);
    }

    // 測試註解啟動
    @Test
    public void testSpring3() {
        sayHello3.sayHello();
        Assert.assertNotNull(sayHello3);
    }

}

最後附上匯入的包

 <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.21.0</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.unitils/unitils-core -->
        <dependency>
            <groupId>org.unitils</groupId>
            <artifactId>unitils-core</artifactId>
            <version>3.4.2</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.unitils/unitils-spring -->
        <dependency>
            <groupId>org.unitils</groupId>
            <artifactId>unitils-spring</artifactId>
            <version>3.4.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.unitils/unitils-testng -->
        <dependency>
            <groupId>org.unitils</groupId>
            <artifactId>unitils-testng</artifactId>
            <version>3.3</version>
        </dependency>