前面项目已创建好,依赖包添加完成,testng也已添加

项目结构如下:

TestNG接口自动化 java java自动化接口测试_java


1.设计配置文件

在src/main/java下新建一个包:com.qa.config,然后在新包下新建一个config.properties文件,文件内容如下

TestNG接口自动化 java java自动化接口测试_json_02


2.加载读取properties文件

然后在src/main/java下新建一个包:com.qa.base,新建一个TestBase.java,这个类作为所有接口请求测试的父类,都需要继承这个父类,目前我们就写一个构造方法,实现加载读取properties文件。

我们不想在代码里写死例如像HTTP响应状态码200这样的硬编码,所以,这里我们在TestBase.java里把状态码给用常量写出来,方便每一个TestNG测试用例去调用去断言

package com.qa.base;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

//接口请求测试的父类
public class TestBase {
		public Properties prop;
	
	//写一个构造函数
	public TestBase() {
		
		try {
			prop = new Properties();
			FileInputStream fis = new FileInputStream(System.getProperty("user.dir")+
 "/src/main/java/com/qa/config/config.properties");
			prop.load(fis);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	
}

3. 新建一个log4j.properties文件
在Eclipse上点击当前项目名,右键new -source folder,输出src/main/config,点击确定,然后在src/main/config下新建一个log4j.properties文件,内容如下。

### set log levels ###
log4j.rootLogger = INFO, stdout, file
 
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss SSS} %-5p %c{1}:%L - %m%n
 
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = ./log/apilog.log
# overwirte the old log file
log4j.appender.file.Append = false      
## 
log4j.appender.file.Threshold = INFO
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss SSS} %-5p %c{1}:%L - %m%n

在项目根目录下新建log文件夹:用于日志输出存放
新建这个文件夹是上面log4j.properties文件我们设置的日志保存文件路径是在./log文件夹下

4.写一个JSON解析的工具类
在src/main/java下新建一个包:com.qa.util,然后在新包下创建一个TestUtil.java类。

package com.qa.util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
 
 
public class TestUtil {
 
   /**
    *
    * @param responseJson ,这个变量是拿到响应字符串通过json转换成json对象
    * @param jpath,这个jpath指的是用户想要查询json对象的值的路径写法
    * jpath写法举例:1) per_page  2)data[1]/first_name ,data是一个json数组,[1]表示索引
    * /first_name 表示data数组下某一个元素下的json对象的名称为first_name
    * @return,返回first_name这个json对象名称对应的值
    */
 
  
   //1 json解析方法
 
   public static String getValueByJPath(JSONObject responseJson, String jpath){
 
      Objectobj = responseJson;
 
      for(String s : jpath.split("/")) {
 
        if(!s.isEmpty()) {
 
           if(!(s.contains("[") || s.contains("]"))) {
 
              obj = ((JSONObject) obj).get(s);
 
           }else if(s.contains("[") || s.contains("]")) {
 
              obj =((JSONArray)((JSONObject)obj).get(s.split("\\[")[0])).get(Integer.parseInt(s.split("\\[")[1].replaceAll("]", "")));
 
           }
        }
      }
      return obj.toString();
   }
}

TestNG接口自动化 java java自动化接口测试_java_03

简单解释下上面的代码,主要是查询两种json对象的的值,第一种最简单的,这个json对象在整个json串的第一层,例如上面截图中的per_page,这个per_page就是通过jpath这个参数传入,返回的结果就是3. 第二种jpath的查询,例如我想查询data下第一个用户信息里面的first_name的值,这个时候jpath的写法就是data[0]/first_name,查询结果应该是Eve。

4.请求方法代码封装
在src/main/java下新建一个包:com.qa.restclient,然后新建一个RestClient.java文件,下面是具体代码,已加日志输出
这个Java接口自动化测试框架的核心就是Get和POST请求方法的封装过程。

package com.qa.restclient;
 
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
 
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
 
public class RestClient {
	
	final static Logger Log = Logger.getLogger(RestClient.class);
	
	/**
	 * 不带请求头的get方法封装
	 * @param url
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse get (String url) throws ClientProtocolException, IOException {
		
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpGet的请求对象
		HttpGet httpget = new HttpGet(url);
		//执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
		Log.info("开始发送get请求...");
		CloseableHttpResponse httpResponse = httpclient.execute(httpget);
		Log.info("发送请求成功!开始得到响应对象。");
		return httpResponse;
	}
	
	/**
	 * 带请求头信息的get方法
	 * @param url
	 * @param headermap,键值对形式
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse get (String url,HashMap<String,String> headermap) throws ClientProtocolException, IOException {
			
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpGet的请求对象
		HttpGet httpget = new HttpGet(url);
		//加载请求头到httpget对象
		for(Map.Entry<String, String> entry : headermap.entrySet()) {
			httpget.addHeader(entry.getKey(), entry.getValue());
		}
		//执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
		CloseableHttpResponse httpResponse = httpclient.execute(httpget);
		Log.info("开始发送带请求头的get请求...");	
		return httpResponse;
	}
	
	/**
	 * 封装post方法
	 * @param url
	 * @param entityString,其实就是设置请求json参数
	 * @param headermap,带请求头
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse post (String url, String entityString, HashMap<String,String> headermap) throws ClientProtocolException, IOException {
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpPost的请求对象
		HttpPost httppost = new HttpPost(url);
		//设置payload
		httppost.setEntity(new StringEntity(entityString));
		
		//加载请求头到httppost对象
		for(Map.Entry<String, String> entry : headermap.entrySet()) {
			httppost.addHeader(entry.getKey(), entry.getValue());
		}
		//发送post请求
		CloseableHttpResponse httpResponse = httpclient.execute(httppost);
		Log.info("开始发送post请求");
		return httpResponse;
	}
	
	/**
	 * 封装 put请求方法,参数和post方法一样
	 * @param url
	 * @param entityString,这个主要是设置payload,一般来说就是json串
	 * @param headerMap,带请求的头信息,格式是键值对,所以这里使用hashmap
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse put (String url, String entityString, HashMap<String,String> headerMap) throws ClientProtocolException, IOException {
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPut httpput = new HttpPut(url);
		httpput.setEntity(new StringEntity(entityString));
	
		for(Map.Entry<String, String> entry : headerMap.entrySet()) {
			httpput.addHeader(entry.getKey(), entry.getValue());
		}
		//发送put请求
		CloseableHttpResponse httpResponse = httpclient.execute(httpput);
		return httpResponse;
	}
	
	/**
	 * 封装 delete请求方法,参数和get方法一样
	 * @param url, 接口url完整地址
	 * @return,返回一个response对象,方便进行得到状态码和json解析动作
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse delete (String url) throws ClientProtocolException, IOException {
			
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpDelete httpdel = new HttpDelete(url);
		
		//发送delete请求
		CloseableHttpResponse httpResponse = httpclient.execute(httpdel);
		return httpResponse;
	}
	
	/**
	 * 获取响应状态码,常用来和TestBase中定义的状态码常量去测试断言使用
	 * @param response
	 * @return 返回int类型状态码
	 */
	public int getStatusCode (CloseableHttpResponse response) {
		
		int statusCode = response.getStatusLine().getStatusCode();
		Log.info("解析,得到响应状态码:"+ statusCode);
		return statusCode;
		
	}
	
	/**
	 * 
	 * @param response, 任何请求返回返回的响应对象
	 * @return, 返回响应体的json格式对象,方便接下来对JSON对象内容解析
	 * 接下来,一般会继续调用TestUtil类下的json解析方法得到某一个json对象的值
	 * @throws ParseException
	 * @throws IOException
	 */
	public JSONObject getResponseJson (CloseableHttpResponse response) throws ParseException, IOException {
		Log.info("得到响应对象的String格式");
		String responseString = EntityUtils.toString(response.getEntity(),"UTF-8");
		JSONObject responseJson = JSON.parseObject(responseString);
		Log.info("返回响应内容的JSON格式");
		return responseJson;
	}
}

5.TestNG测试用例测试get方法
在src/test/java下新建一个包:com.qa.tests,然后新建一个GetApiTest.java类,写一个TestNG的测试用例来测试下我们上面写的Get请求方法
简单提一下TestNG的断言方法,我们一般测试都需要写断言的代码,否则这样的单元测试代码就没有意义。下面,我在statusCode和json解析的first_name进行断言。

package com.qa.tests;
 
import java.io.IOException;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.alibaba.fastjson.JSONObject;
import com.qa.base.TestBase;
import com.qa.restclient.RestClient;
import com.qa.util.TestUtil;
 
public class GetApiTest extends TestBase {
	
	TestBase testBase;
	String host;
	String url;
	RestClient restClient;
	CloseableHttpResponse closeableHttpResponse;
	
	final static Logger Log = Logger.getLogger(GetApiTest.class);
	
	@BeforeClass
	public void setUp() {
		
		testBase = new TestBase();
		//Log.info("测试服务器地址为:"+ host.toString());
		host = prop.getProperty("HOST");
		//Log.info("当前测试接口的完整地址为:"+url.toString());
		url = host + "/api/users?page=2";
		
	}
	
	@Test
	public void getAPITest() throws ClientProtocolException, IOException {
		Log.info("开始执行用例...");
		restClient = new RestClient();
		closeableHttpResponse = restClient.get(url);
		
		//断言状态码是不是200
		Log.info("测试响应状态码是否是200");
		int statusCode = restClient.getStatusCode(closeableHttpResponse);
		Assert.assertEquals(statusCode, RESPNSE_STATUS_CODE_200, "response status code is not 200");
		
        JSONObject responseJson = restClient.getResponseJson(closeableHttpResponse);
        //System.out.println("respon json from API-->" + responseJson); 
        
        //json内容解析
        String s = TestUtil.getValueByJPath(responseJson,"data[0]/first_name");
        Log.info("执行JSON解析,解析的内容是 " + s);
        //System.out.println(s);
        Log.info("接口内容响应断言");
        Assert.assertEquals(s, "Eve","first name is not Eve");
        Log.info("用例执行结束...");
	}
	
	
}

执行日志输出如下:

TestNG接口自动化 java java自动化接口测试_java_04

经常使用的测试断言:

Assert.assertEquals(“现实结果”, “期待结果”,“断言失败时候打印日志消息”);

6.TestNG测试用例测试post方法

post接口示例:

TestNG接口自动化 java java自动化接口测试_json_05


这个接口的作用是创建用户,参数是一个json类型的数据,一个name一个job,两个JSON对象。发送请求之后,返回的JSON数据有name和job和id,以及创建时间这几个数据。

JAVA Bean类

写测试用例之前,我们需要提前准备好json数据【上边的name和job】,一般来说,在Java中JSON数据都是放在JAVA Bean类中,通过JSON把高级对象序列化成JSON对象。

在src/main/java中新建包:com.qa.data,然后新建一个Users.java,这个命名就参考接口的url单词就行。在postman或者网站该post方法,我们知道,需要name和job这两个json对象。我们新建一个bean类,同alt+shift+s,然后选择生成构造方法和set和get方法。

package com.qa.data;

public class Users {
	private String name;
	private String pwd;
	private String srv_name;
	
	public Users() {
		super();
	}
 
	public Users(String name, String job,String srv_name) {
		super();
		this.name = name;
		this.pwd = pwd;
		this.srv_name = srv_name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getSrv_name() {
		return srv_name;
	}

	public void setSrv_name(String srv_name) {
		this.srv_name = srv_name;
	}
 
	
}

TestNG用例测试
在src/test/java下的com.qa.tests我们新建一个POST测试用例,现在我们的TestNG测试类代码如下:

package com.qa.tests;
 
import java.io.IOException;
import java.util.HashMap;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qa.base.TestBase;
import com.qa.data.Users;
import com.qa.restclient.RestClient;
import com.qa.util.TestUtil;
 
public class PostApiTest extends TestBase {
	TestBase testBase;
	String host;
	String url;
	RestClient restClient;
	CloseableHttpResponse closeableHttpResponse;
	
	
	@BeforeClass
	public void setUp() {
		testBase = new TestBase();
		host = prop.getProperty("HOST");
		url = host + "/api/users";
		
	}
	
	@Test
	public void postApiTest() throws ClientProtocolException, IOException {
		restClient = new RestClient();
		//准备请求头信息
		HashMap<String,String> headermap = new HashMap<String,String>();
		headermap.put("Content-Type", "application/json"); //这个在postman中可以查询到
		
		//对象转换成Json字符串
		Users user = new Users("Anthony","tester");
		String userJsonString = JSON.toJSONString(user);
		//System.out.println(userJsonString);
		
		closeableHttpResponse = restClient.post(url, userJsonString, headermap);
		
		//验证状态码是不是200
		int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
		Assert.assertEquals(statusCode, RESPNSE_STATUS_CODE_201,"status code is not 201");
		
		//断言响应json内容中name和job是不是期待结果
		String responseString = EntityUtils.toString(closeableHttpResponse.getEntity());
		JSONObject responseJson = JSON.parseObject(responseString);
		//System.out.println(responseString);
		String name = TestUtil.getValueByJPath(responseJson, "name");
		String job = TestUtil.getValueByJPath(responseJson, "job");
		Assert.assertEquals(name, "Anthony","name is not same");
		Assert.assertEquals(job, "tester","job is not same");
		
	}
 
}

7.TestNG测试用例测试put方法

package com.qa.tests;
 
import java.io.IOException;
import java.util.HashMap;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qa.base.TestBase;
import com.qa.data.Users;
import com.qa.restclient.RestClient;
import com.qa.util.TestUtil;
 
public class PutApiTest extends TestBase {
	
	TestBase testBase;
	String host;
	String url;
	RestClient restClient;
	CloseableHttpResponse closeableHttpResponse;
	
	
	@BeforeClass
	public void setUp() {
		testBase = new TestBase();
		host = prop.getProperty("HOST");
		url = host + "/api/users/2";
		
	}
	
	@Test
	public void putTest() throws ClientProtocolException, IOException{
		restClient = new RestClient();
		HashMap<String,String> headermap = new HashMap<String,String>();
		headermap.put("Content-Type", "application/json"); //这个在postman中可以查询到
		
		//对象转换成Json字符串
		Users user = new Users("Anthony","automation tester");
		String userJsonString = JSON.toJSONString(user);
		//System.out.println(userJsonString);
		
		closeableHttpResponse = restClient.put(url, userJsonString, headermap);
		
		//验证状态码是不是200
		int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
		Assert.assertEquals(statusCode, RESPNSE_STATUS_CODE_200,"response status code is not 200");
		
		//验证名称为Anthony的jon是不是 automation tester
		String responseString = EntityUtils.toString(closeableHttpResponse.getEntity());
		JSONObject responseJson = JSON.parseObject(responseString);
		String jobString = TestUtil.getValueByJPath(responseJson, "job");
		System.out.println(jobString);
		Assert.assertEquals(jobString, "automation tester","job is not same");
	}
 
}

8.TestNG测试用例测试Delete方法

package com.qa.tests;
 
import java.io.IOException;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
import com.qa.base.TestBase;
import com.qa.restclient.RestClient;
 
public class DeleteApiTest extends TestBase {
	TestBase testBase;
	String host;
	String url;
	RestClient restClient;
	CloseableHttpResponse closeableHttpResponse;
	
	
	@BeforeClass
	public void setUp() {
		testBase = new TestBase();
		host = prop.getProperty("HOST");
		url = host + "/api/users/2";  //直接在这个网站可以找到delete的api
		
	}
	
	@Test
	public void deleteApiTest() throws ClientProtocolException, IOException {
		restClient = new RestClient();
		closeableHttpResponse = restClient.delete(url);
		
		int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
		System.out.println(statusCode);
		Assert.assertEquals(statusCode, 204,"status code is not 204");
	}
}