Java接口自动化测试之TestNg运行时周期

上一篇自动化测试我们大概了解了测试的目标、测试的技术选型以及搭建平台的目标及需求,也确定了自动
化测试方案以testNg作为整个测试流程贯穿的基础支持框架,那么testNg究竟有什么特点?本篇开始我们来详
细的学习testNg这个测试框架

为什么要用testNg

首先我们学习之前,先思考一个问题,java测试的框架很多,为什么要用testNg呢?比如junit,使用的人很多,几乎所有java开发人员都会使用junit作为测试框架,那么我们从以下三个点来大概了解下testNg比起junit不同的地方:

1.灵活的方法命名

junit使用过的人应该都知道,申明一个测试方法的传统方式是必须在方法名加一个test前缀,但是限制了测试方法的命名,的确从灵活度的角度来说不太适合,而TestNg完全不限制测试方法名,完全通过注解的方式查找测试方法

2.方法支持灵活动态传参

熟悉junit的人肯定知道,junit方法默认不支持传递参数,那么我们为了传递参数给方法,往往会选择其他的所谓设计模式技术,比如有参构造等方式避开构建参数的问题,可以说此种方式几乎断绝了我们使用junit做方法传递,方法连续性调用等集成测试的念头,而testNg则不同,不仅支持方法传递参数,而且支持 DataProvider等动态构建数据的方式(后面介绍),并且还支持方法依赖、组依赖等策略,可以灵活的将不同方法之间组合调用,实现单元测试以及集成测试等

3.testNg支持多种模式多种扩展

junit不用多说,仅提供很简单的测试方式,本身提供的扩展能力很弱,在遇到复杂测试或者定制化测试需求的时候,完全无从下手,而testNg支持xml、注解两种配置方式,且两种方式可以混合配置,并且在运行的周期内提供了多个注解和拦截、选择器等扩展机制,可以灵活的扩展和选择排除的方法

第一个testNg测试方法

介绍了那么多testNg的优点,那么我们开始第一个简单的测试方法编写吧,打开我们上一篇创建的测试工程,我们首先进行一部分的修改,这里我们选择把测试的范围放开,并且将测试方法变成src/main目录下的正常开发代码使用(因为这里将testNg自动化测试作为某个工程项目进行开发迭代),所以我们需要将pom文件中的:

<dependency><groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

scope范围标签注释掉,这样即可将test模块覆盖到整个工程,接着我们创建一个conf包,用来保存我们整个测试过程中遇到的所有的测试基础配置,现在我们在conf包下创建一个基础类--BaseTestNg,切继承AbstractTestNGSpringContextTests类,这个类可以用来获取测试过程中的运行周期和运行时参数与上下文等操作,这些方法对于扩展测试和负载测试相当重要,现在我们只需要继承即可:

/**
 * 基础测试父类-用来获取上下文、运行时参数、注入等
 */
public class BaseTestNg extends AbstractTestNGSpringContextTests {
    
}

然后创建一个test包,在此包下创建一个demo01类,开始编写一个简单的测试方法吧:

public class demo01 extends BaseTestNg{

    @Test
    public void tDemo(){
        System.out.println("当前运行的是第一个测试方法");
    }
}

这里需要注意一点,@Test注解使用org.testng.annotations.Test包下的,并且这里可以看到,我们方法命名并没有按照test开头,现在我们运行一下这个方法:

第一个测试方法.png

运行成功了!咦,这个tDemo上面的demo01、test以及Default Suite分别是什么?为什么有这些东西呢?相信细心的你已经发现了,这里需要涉及testNg的一个运行机制,在testNg中会运行一个Suite测试套件,每个套件中可以存在多个test测试模块,并且每个test下可以存在多种策略,比如class或者方法等,但是我们刚才运行的仅仅是tDemo这一个方法而已,所以testNg在启动的时候,会按照默认值的方式,将当前方法挂载在名叫demo01的测试类上,并且将当前测试类挂载在名为test的测试模块上,在所有测试的模块中都会默认挂载在对应的Suite上,这里默认的即Default Suite

测试运行周期

刚刚我们运行了一个简单的测试方法,那么我们如何做灵活的方法控制呢?比如在测试之前我们需要做一些资源的准备工作,并且在测试完成以后,我们又需要做一些销毁资源释放内存的操作,怎么办?针对这一点,testNg中提供了一系列可以依赖的运行时方法,可以在不同的运行时生命周期中进行执行,目前支持的注解方法如下:

@BeforeSuite

@BeforeSuite注解方法在套件运行之前执行

@BeforeTest

@BeforeTest注解方法在测试用例之前执行

@BeforeClass

@BeforeClass注解方法在测试类运行之前执行

@BeforeMethod

@BeforeMethod注解方法在测试方法运行之前执行

@AfterSuite

@AfterSuite注解方法在测试套件运行结束以后执行

@AfterTest

@AfterTest注解方法在测试用例结束以后执行

@AfterClass

@AfterClass注解方法在测试类运行结束以后执行

@AfterMethod

@AfterMethod注解方法在测试方法运行结束以后执行


那么,这些注解方法执行顺序是什么呢?与测试用例、测试方法的关系是什么呢?我们来写一个demo测试一下:

/**
 * 测试运行周期
 */
public class demoTest extends BaseTestNg {
    //在套件运行之前执行
    @BeforeSuite
    public void BeforeSuite(){
       System.out.println("BeforeSuite");
    }
    //在测试用例之前执行
    @BeforeTest
    public void BeforeTest(){
        System.out.println("BeforeTest");
    }
    //在测试类运行之前执行
    @BeforeClass
    public void BeforeClass(){
        System.out.println("BeforeClass");
    }
    //在测试方法运行之前执行
    @BeforeMethod
    public void BeforeMethod(){
        System.out.println("BeforeMethod");
    }
    //在测试套件运行结束以后执行
    @AfterSuite
    public void AfterSuite(){
        System.out.println("AfterSuite");
    }
    //在测试用例结束以后执行
    @AfterTest
    public void AfterTest(){
        System.out.println("AfterTest");
    }
    //在测试类运行结束以后执行
    @AfterClass
    public void AfterClass(){
        System.out.println("AfterClass");
    }
    //在测试方法运行结束以后执行
    @AfterMethod
    public void AfterMethod(){
        System.out.println("AfterMethod");
    }

    @Test
    public void test(){
        System.out.println("test");
    }
}

编写以后,我们来运行这个测试方法,可以看到输出结果:

BeforeSuite
BeforeTest
BeforeClass
BeforeMethod
test
AfterMethod
AfterClass
AfterTest
AfterSuite

可以看出来运行时周期执行顺序为:

BeforeSuite-->BeforeTest-->BeforeClass-->BeforeMethod-->测试方法-->AfterMethod-->AfterClass-->AfterTest-->AfterSuite

推荐阅读更多精彩内容