TestNG进行接口测试,脚本及可维护性框架

testng被普遍使用于基于java和spring的系统结构中,用于保证系统功能,本身testng的特点:

1.结构清晰

2.支持多种数据源

3.可与maven集成

4.环境/数据准备方便

可用于系统中对外提供的接口进行接口测试脚本的编写(单元测试则一般用junit完成)。


经典的测试脚本,一般分为三个步骤:

1.数据和环境初始化;

2.执行被测接口调用;

3.断言判断。

负责任和健壮的测试脚本,一般还要进行测试数据初始化,测试依赖的环境的调整(如业务开关打开),在执行完测试脚本后,进行数据的清理,如环境的恢复测试遗留数据的清理

下面记录一个简单的接口测试编写过程:

被测接口:

package net.test.testng.service;

/**
 * interface to be tested.
 * @author T0DD
 *
 */
public interface ToBeTestedService {
	/**
	 * return a string.
	 * @return 
	 */
	public String returnString();
	
	/**
	 * return a obj
	 * @return
	 */
	public Object returnObject();

}

被测接口实现:

package net.test.testng.service.impl;

import java.sql.SQLException;

import net.test.testng.model.Result;
import net.test.testng.service.DataBaseDAO;
import net.test.testng.service.FunctionSwitch;
import net.test.testng.service.ToBeTestedService;

public class ToBeTestedServicezImpl implements ToBeTestedService {

	protected DataBaseDAO dataBaseDAO;

	protected FunctionSwitch functionSwitch;

	@Override
	public String returnString() {
		return "a string";
	}

	@Override
	public Object returnObject() {

		functionSwitch.setAvaiable(true);

		Object queriedData = null;
		try {
			queriedData = dataBaseDAO.readData("select * from test db;");
		} catch (SQLException e) {
			return new Result(false, "DB error.", null);
		}

		return new Result(true, "Switch turned on and queried successed.",
				queriedData);
	}

}

其中两个方法,一个非常简单的方法,returnString,只返回一个“a string";另一个,根据业务开关代开与否,进行数据库的读操作,组装result对象返回,result定义为:

package net.test.testng.model;

public class Result {

	public boolean isSuccess;

	public String errorMsg;

	public String succesMsg;

	public Object data;

	public Result(boolean isSuccess, String msg, Object data) {
		super();
		this.isSuccess = isSuccess;
		if (isSuccess) {
			this.succesMsg = msg;
		} else {
			this.errorMsg = msg;
		}
		this.data = data;
	}

	public boolean isSuccess() {
		return isSuccess;
	}

	public void setSuccess(boolean isSuccess) {
		this.isSuccess = isSuccess;
	}

	public String getErrorMsg() {
		return errorMsg;
	}

	public void setErrorMsg(String errorMsg) {
		this.errorMsg = errorMsg;
	}

	public String getSuccesMsg() {
		return succesMsg;
	}

	public void setSuccesMsg(String succesMsg) {
		this.succesMsg = succesMsg;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

}
重点,测试类
package net.test.testng.servicetest;

import java.sql.SQLException;

import net.test.testng.model.Result;
import net.test.testng.service.DataBaseDAO;
import net.test.testng.service.FunctionSwitch;
import net.test.testng.service.ToBeTestedService;

import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class ToBeTestedServiceTest {

	protected FunctionSwitch functionSwitch;

	protected DataBaseDAO dataBaseDAO;

	protected boolean env;

	protected ToBeTestedService toBeTestedService;

	@BeforeClass
	public void initEnv() {
		env = functionSwitch.isAvaiable();
		if (functionSwitch.setAvaiable(true)) {
			Assert.fail("Prepare environment error!");
		}
	}

	@BeforeMethod
	public void initData() {
		try {
			dataBaseDAO.insert("insert into db data...");
		} catch (SQLException e) {
			Assert.fail("Init database error!");
		}
	}

	@Test
	public void testReturnString() {

		String result = toBeTestedService.returnString();

		Assert.assertNotNull(result);

		Assert.assertEquals(result, "a string");
	}

	@Test
	public void testReturnObject() {
		
		Object result = toBeTestedService.returnObject();

		Assert.assertNotNull(result);

		if (!(result instanceof Result)) {
			Assert.fail("Error return type.");
		}

		Assert.assertTrue(((Result) result).isSuccess());

		Assert.assertNotNull(((Result) result).getSuccesMsg());

		Object dbData = null;
		try {
			dbData = dataBaseDAO.readData("insert into db data...");
		} catch (SQLException e) {
			Assert.fail("read data from db error.");
		}
		Assert.assertEquals(((Result) result).getData(), dbData);

	}

	@AfterMethod
	public void cleanData() {
		try {
			dataBaseDAO.delete("insert into db data...");
		} catch (SQLException e) {
			Assert.fail("Delete database error!");
		}
	}

	@AfterClass
	public void restoreEnv() {
		functionSwitch.setAvaiable(env);
	}

}

说明

1.其中beforeClass注解下,为整个test case运行所需要的基本环境,包括业务开关,上下游业务中所需要的其他服务的引入以及服务引入成功的判断,如果有一个服务都没有引入成功,其实该test case就没有必要执行,也就不需要浪费测试资源去执行了。

2.beforeMethod注解下,可以提供本次测试方法(服务中有两个方法,其中一个就依赖了数据库中的内容,所以要初始化数据库。)所依赖的环境信息。

3.两个after中,依次对数据和环境进行恢复,在CI情况下,本次执行的结果 有可能会影响到下次执行,所以,负责任的方法是,执行完本次test case后,就相应的进行环境和数据的清理,便于下次接口测试的顺利执行。

建议

1.接口测试脚本的结构很重要,对于业务场景相似的情况,可以设置测试基类,将环境/数据的初始化放置在基类中;将测试执行和测试结果判断适当加以区分,这样逻辑会清晰,便于测试脚本维护。

2.提高脚本复用程度,如采取参数化驱动,使用csv/txt/excel构建测试数据驱动,编写自己的dataProvider,解析测试数据,提高脚本的利用率,如上述脚本中,针对returnObject方法,可以构建

a.数据初始化另一个返回结果

b.数据初始化为空

c.业务开关关闭

等情况,并且测试数据和测试结果一一对应,都进行参数化,加上正常场景,相当于进行了4个业务场景的测试,能够极大减少回归测试的人工。

3.为脚本执行后的副作用负责,主要是指,在脚本执行的过程中,难免会引入一些测试数据和对当前业务流的改动,在执行完成后,需要将这些执行痕迹清理和还原,一是便于手工测试的正常执行,而是为下次持续集成,减轻了错误概率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值