JUnit是一个开发源代码的Java测试框架,用于编写和运行可重复的测试。它是用于单元测试框架体系xUnit的一个实例(用于java语言)。主要用于白盒测试,回归测试。
个人理解:每次软件做版本迭代,为防止引入新的问题,需要对重要功能的接口进行基本功能测试。此时可以考虑做成自动化(在版本迭代较快,主要功能基本不变化的接口适用)
之前本人测试使用JUnit3,目前JUnit4已经普及。JUnit 4是与JUnit3完全不同的API,它基于Java 5.0中的注解、静态导入等构建而成。JUnit 4更简单、更丰富、更易于使用,并引入了更为灵活的初始化和清理工作,还有限时的和参数化测试用例
JUnit3下载地址:https://pan.baidu.com/s/1jH9Kv90
JUnit4下载地址:https://pan.baidu.com/s/1qYLZC7a
在eclipse中新建工程,然后在configure build path中导入junit jar包,在eclipse的工程中引入Junit lib库,然后可以创建Junit Test Case或者Junit Test Suite
这里展示测试用例的编写过程
测试要求
某数据平台提供接口给第三方使用,第三方根据接口托管自己的数据。需要测试平台提供的所有对外接口的功能,确保正常
准备工作
获取提供给第三方的Api(jar包形式),在eclipse中新建工程,导入该jar包,导入JUnit包,新建package,针对每个方法创建对应的测试类等,如下图所示
目录结构及类说明
在cn.cstor.wwy.test存放测试类,每个方法对应一个测试类,比如平台提供AddDevice接口,所以要新建一个TestAddDevice类,在TestAddDevice类中存在多个测试方法
package cn.cstor.wwy.test;
import cn.cstor.wwy.test.util.Count;
import java.io.UnsupportedEncodingException;
import org.apache.thrift.TException;
import cn.cstor.wwy.test.util.Const;
import cn.cstor.wwy.test.util.CustomString;
import cproc.datacube.client.api.ClientBaseApi;
import cproc.datacube.client.api.ClientBaseApiService;
import cproc.datacube.client.api.ClientDevelopApi;
import cproc.datacube.client.api.ClientDevelopApiService;
import cproc.datacube.client.entity.AppInfo;
import cproc.datacube.client.entity.DeviceConditionInfo;
import cproc.datacube.client.entity.ReObject;
import cproc.datacube.client.entity.UserInfo;
import junit.framework.TestCase;
import java.lang.Math;
//addDevice
public class TestAddDevice extends TestCase{
CustomString cString = new CustomString();
AppInfo app = new AppInfo();
UserInfo user = new UserInfo();
ClientDevelopApi deveApi = new ClientDevelopApiService();
ClientBaseApi baseApi = new ClientBaseApiService();
Count ct = new Count();
int startct = 0;
int endct = 0;
protected void setUp() throws UnsupportedEncodingException, TException
{
user.setUserName(Const.USERNAME);
user.setAccessId(Const.CERTIFICATE);
app.setAppId(Const.APPID);
app.setUserInfo(user);
}
protected void tearDown() throws TException
{
}
//正常用例
public void testAddDevice() throws TException
{
//设置设备信息
startct = ct.getPoint();
DeviceConditionInfo deviceCon = new DeviceConditionInfo();
int x = (int)(Math.random()*Math.random()*Math.random()*1000000);
deviceCon.setDeviceid("device" + x);
deviceCon.setDip("192.168.10.1");
deviceCon.setServicecode("1");
deviceCon.setVrs("v0.1");
ReObject reObject = deveApi.addDevice(app,deviceCon);
endct = ct.getPoint();
System.out.println(startct);
System.out.println(endct);
assertEquals(true,reObject.isSuccess());
assertEquals(startct - 1,endct);
}
//设备名为空
public void testAddDeviceNullDevId() throws TException
{
startct = ct.getPoint();
DeviceConditionInfo deviceCon = new DeviceConditionInfo();
deviceCon.setDeviceid("");
deviceCon.setDip("192.168.10.1");
deviceCon.setServicecode("1");
deviceCon.setVrs("v0.1");
ReObject robj = deveApi.addDevice(app,deviceCon);
endct = ct.getPoint();
assertEquals(false,robj.isSuccess());
assertEquals("查询条件不足",robj.getMsg());
assertEquals(startct,endct);
}
//设备名称太长
public void testAddDeviceLongDevName() throws TException
{
startct = ct.getPoint();
DeviceConditionInfo deviceCon = new DeviceConditionInfo();
String devid = cString.generateString(200);
deviceCon.setDeviceid(devid);
deviceCon.setDip("192.168.10.1");
deviceCon.setServicecode("1");
deviceCon.setVrs("v0.1");
ReObject reObject = deveApi.addDevice(app,deviceCon);
endct = ct.getPoint();
assertEquals(false,reObject.isSuccess());
assertEquals("插入数据失败!",reObject.getMsg());
assertEquals(startct,endct);
}
//ip地址为空
public void testAddDeviceNullIP() throws TException
{
//设置设备信息
startct = ct.getPoint();
DeviceConditionInfo deviceCon = new DeviceConditionInfo();
int x = (int)(Math.random()*Math.random()*Math.random()*1000000);
deviceCon.setDeviceid("device" + x);
deviceCon.setDip("");
deviceCon.setServicecode("1");
deviceCon.setVrs("v0.1");
ReObject reObject = deveApi.addDevice(app,deviceCon);
endct = ct.getPoint();
assertEquals(true,reObject.isSuccess());
assertEquals(startct - 1,endct);
}
//ip格式不正确
public void testAddDeviceWrongIP() throws TException
{
//设置设备信息
startct = ct.getPoint();
DeviceConditionInfo deviceCon = new DeviceConditionInfo();
int x = (int)(Math.random()*Math.random()*Math.random()*1000000);
deviceCon.setDeviceid("device" + x);
deviceCon.setDip("123.12.25.23.212");
deviceCon.setServicecode("1");
deviceCon.setVrs("v0.1");
ReObject reObject = deveApi.addDevice(app,deviceCon);
endct = ct.getPoint();
assertEquals(true,reObject.isSuccess());
assertEquals(startct - 1,endct);
}
}
在cn.cstor.wwy.test.util中存放一些常量信息
Const.java:存放用户认证信息,应用信息,测试创建表名
package cn.cstor.wwy.test.util;
public interface Const {
// user info
String USERNAME = "shenpp";
String CERTIFICATE = "C2HLBNBWMTUWMDI1NDCXNTI2MQ==";
String APPID = "0542051559";
String TABLENAME="CREATEBYINTERFACE";
}
Count.java:获取积分方法 //测试需要对比积分,但是没有借口查询,只能通过web页面查询,故增加该方法
package cn.cstor.wwy.test.util;
public class Count {
public int getPoint()
{
int count = 0;
String url = "http://192.168.10.105:8080/queryPointsForTest?uid=3450&accessid=C2HLBNBWMTUWMDI1NDCXNTI2MQ==";
try
{
String result = HttpUtil.getHttpContent(url,"");
String[] res1 = result.split(":");
String[] res2 = res1[1].split("}");
count = Integer.valueOf(res2[0]).intValue();
} catch (Exception e)
{
e.printStackTrace();
}
return count;
}
}
CustomString.java :生成一段随机字符串,测试使用
package cn.cstor.wwy.test.util;
import java.util.Random;
public class CustomString {
public String generateString(int clength) {
String base = "abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < clength; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
HttpUtil.java :设置http访问的参数
package cn.cstor.wwy.test.util;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtil {
public static String getHttpContent(String url,String parameterData ) throws Exception {
HttpURLConnection connection = null;
// String content = "";
OutputStream outputStream = null;
OutputStreamWriter outputStreamWriter = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader reader = null;
StringBuffer resultBuffer = new StringBuffer();
String tempLine = null;
try {
URL address_url = new URL(url);
connection = (HttpURLConnection) address_url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("Accept-Charset", "utf-8");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", String.valueOf(parameterData.length()));
//设置访问超时时间及读取网页流的超市时间,毫秒值
System.setProperty("sun.net.client.defaultConnectTimeout","3000");
System.setProperty("sun.net.client.defaultReadTimeout", "3000");
outputStream = connection.getOutputStream();
outputStreamWriter = new OutputStreamWriter(outputStream);
outputStreamWriter.write(parameterData);
outputStreamWriter.flush();
if (connection.getResponseCode() >= 300) {
throw new Exception("HTTP Request is not success, Response code is " + connection.getResponseCode());
}
inputStream = connection.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
reader = new BufferedReader(inputStreamReader);
while ((tempLine = reader.readLine()) != null) {
resultBuffer.append(tempLine);
}
}finally {
if(connection !=null){
connection.disconnect();
}
}
return resultBuffer.toString();
}
}
我所理解的接口自动化测试就是能一次性执行上述编写的所有测试类的测试方法,如此可以通过如下方法实现:
在cn.cstor.wwy.test包中新建JUnit Test Suite,名称为TestAll
import junit.framework.Test;
import junit.framework.TestSuite;
public class TestAll {
public static Test suite() {
TestSuite suite = new TestSuite(AllTests.class.getName());
//$JUnit-BEGIN$
//$JUnit-END$
return suite;
}
}
在中间加入所有的测试类,这样执行TestAll.java就能执行所有接口的测试用例
package cn.cstor.wwy.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class TestAll {
public static Test suite() {
TestSuite suite = new TestSuite(TestAll.class.getName());
suite.addTestSuite(TestGetAllAppDevices.class);
suite.addTestSuite(TestCreateTable.class);
suite.addTestSuite(TestGetTableInfo.class);
suite.addTestSuite(TestAddTableColumns.class);
suite.addTestSuite(TestAddTableRows.class);
suite.addTestSuite(TestDeleteTableRows.class);
suite.addTestSuite(TestGetTableRowsByDeveApi.class);
suite.addTestSuite(TestGetTableRowsByBaseApi.class);
suite.addTestSuite(TestGetAllTableInfo.class);
suite.addTestSuite(TestGetTopOneResult.class);
suite.addTestSuite(TestGetTableRowsByRange.class);
suite.addTestSuite(TestGetTableRowsByPKRange.class);
suite.addTestSuite(TestGetTableRowsByNewPageCondition.class);
suite.addTestSuite(TestDeleteTable.class);
suite.addTestSuite(TestAddDevice.class);
suite.addTestSuite(TestGetDeviceTopOneResult.class);
suite.addTestSuite(TestGetDevsStatisticWithTimeDivision.class);
suite.addTestSuite(TestGetHardWareStatisticWithTimeDivision.class);
// suite.addTestSuite(TestGetQueryResult.class);
suite.addTestSuite(TestGetServerCurrentTime.class);
suite.addTestSuite(TestGetStatisticByColumn.class);
suite.addTestSuite(TestGetTableRowsByPage.class);
suite.addTestSuite(TestQueryDeviceDataAvgValues.class);
suite.addTestSuite(TestQueryLongitudeLatitude.class);
return suite;
}
}
@BeforeClass 在测试类被调用前执行,一个测试类中声明一次,执行一次
@AfterClass 在测试类调用结束后执行,一个测试类中声明一次,执行一次
@Before 在每个@Test(测试方法)调用前执行 //Junit3中的setup方法
@After 在每个@Test调用后执行 //Junit3中的teardown方法
@Test 使用此注解的方法是一个单元测试用例
@Ignore 暂时不执行的测试方法
@Test(timeout = 2000) //用例执行时间不能超过2s
@Test(expected = ArithmeticException.class) //此方法执行后,必须抛出ArithmeticException才能认为测试执行成功
assert //包含此关键字的为断言方法
使用TestNG需要在eclipse中安装TestNG插件
在eclipse界面选择“Help”–“install New Software”,选择【Add】,在location中输入http://beust.com/eclipse,选择TestNG,点击【Next】,开始下载安装。安装完成后,选择“Build Path”“Configure Build Path”,在“Task Repository”下有“TestNG”标签。安装成功。(此方法如果eclipse版本或者以前是否安装卸载过的不同会造成此方法成功率很小);
在eclipse界面选择“Help”–“Eclipse Marketplace”中进行查找TestNG 然后进“install” 。(此方法最简便,力荐。);
TestNG需要使用testng.xml配置层级结构
1、TestSuite由一个或多个Test组成
2、Test由一个或多个测试Class组成
3、一个测试Class由一个或多个测试方法组成
<method>
method>
classes>
test>
......
suite>
TestNG常用注解
@BeforeSuite //在当前测试集合的任意测试用例开始前执行
@AfterSuite //在当前测试集合的所以测试用例结束后执行
@BeforeTest //……
@AfterTest
@BeforeGroups
@AfterGroups
@BeforeClass
@AfterClass
@BeforeMethod
@AfterMethod
@Test
给测试方法分组
@Test (groups = {“xx1”}) @Test (groups = {“xx2”})
调用单个分组testng.xml
<suite name="suitetest">
<test name="test">
<groups>
<run>
<include name="xx1"/>
run>
groups>
<classes>
<class name="pp.com.TestClass1"/>
<class name="pp.com.TestClass2"/>
classes>
test>
suite>
调用多个分组testng.xml
<groups>
<define name="All">
<include name="xx1">
<include name="xx2">
define>
<run>
<include name="xx1"/>
run>
groups>
依赖测试
@Test (dependsOnMethods = {“testMethod2”}) //可以保证先执行testMethod2再执行该方法
特定顺序执行测试用例
@Test (priority = 1) //priority从0开始,0优先级最高
跳过摸个测试方法
@Test (priority = 1,enabled=false)
测试报告中的自定义日志
方法中增加 Reporter.log(“打印日志”) 打印出来的日志会在TestNG测试报告中
@Parameters(“browser”) 定义browser参数,在测试执行过程中,此参数的具体值由testng.xml中的
"browser" value="firefox"/>
配置来传递给测试程序