欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

单元测试框架TestNG、Mockito、Unitils-Spring及示例

程序员文章站 2022-04-26 09:40:45
...
一. 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>