├── src
├── main
│ ├── filters
│ │ ├── filter_uat.properties
│ │ └── filter_sit.properties
│ ├── resources
│ │ ├── PublicArgs.properties
│ │ ├── env.properties
│ │ ├── testCase
│ │ │ ├── OpenYg.xls
│ │ │ └── SendmsgYg.xls
│ │ ├── sql
│ │ │ ├── tearDownSQL.sql
│ │ │ └── setUpSQL.sql
│ │ ├── iiaccount_db.properties
│ │ ├── temp
│ │ │ ├── environment.properties
│ │ │ └── categories.json
│ │ ├── testngXml
│ │ │ └── testSuite_YG.xml
│ │ ├── databaseAssert
│ │ │ └── OpenYg.xml
│ │ └── log4j.properties
│ └── java
│ │ └── com
│ │ └── iiaccount
│ │ ├── exceptions
│ │ └── ErrorException.java
│ │ ├── functioins
│ │ ├── Function.java
│ │ ├── PhoneFunction.java
│ │ ├── RandomFunction.java
│ │ └── IdNoFunction.java
│ │ ├── asserts
│ │ ├── ConstVariable.java
│ │ ├── ContainAssert.java
│ │ ├── Asserts.java
│ │ ├── RespondAssertForJson.java
│ │ └── DatabaseAssert.java
│ │ ├── utils
│ │ ├── FileUtil.java
│ │ ├── GainSqlUtil.java
│ │ ├── StringUtil.java
│ │ ├── PhoneUtil.java
│ │ ├── IsNumericUtil.java
│ │ ├── RandomUtil.java
│ │ ├── FunctionUtil.java
│ │ ├── GetFileMess.java
│ │ ├── WritePropertiesUtil.java
│ │ ├── ClassFinder.java
│ │ ├── IdNoUtil.java
│ │ ├── AssembledMessForJson.java
│ │ └── SplitXmlForCaseNo.java
│ │ ├── listener
│ │ ├── FailedRetry.java
│ │ └── FailedRetryListener.java
│ │ ├── dao
│ │ ├── ExcutSqlFile.java
│ │ ├── CRUDData.java
│ │ └── DBDPConnection.java
│ │ ├── common
│ │ ├── RunCaseJson.java
│ │ └── SetUpTearDown.java
│ │ ├── data
│ │ ├── DataProviders.java
│ │ └── DataBuilders.java
│ │ └── report
│ │ └── TestStep.java
└── test
│ └── java
│ └── com
│ └── iiaccout
│ └── yiguan
│ ├── SendmsgYg.java
│ └── OpenYg.java
├── .idea
├── vcs.xml
└── compiler.xml
├── README.md
└── pom.xml
/src/main/filters/filter_uat.properties:
--------------------------------------------------------------------------------
1 | Environment=uat
--------------------------------------------------------------------------------
/src/main/resources/PublicArgs.properties:
--------------------------------------------------------------------------------
1 | #接口公共入参,以英文逗号隔开
2 | pubArgsYg=api,version
--------------------------------------------------------------------------------
/src/main/resources/env.properties:
--------------------------------------------------------------------------------
1 | Environment=${Environment}
2 | baseURI=${baseURI}
3 | basePath=${basePath}
4 | port=${port}
--------------------------------------------------------------------------------
/src/main/resources/testCase/OpenYg.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tomandy08/ApiAutoTest/HEAD/src/main/resources/testCase/OpenYg.xls
--------------------------------------------------------------------------------
/src/main/resources/testCase/SendmsgYg.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tomandy08/ApiAutoTest/HEAD/src/main/resources/testCase/SendmsgYg.xls
--------------------------------------------------------------------------------
/src/main/resources/sql/tearDownSQL.sql:
--------------------------------------------------------------------------------
1 | -- 案例执行后数据清理
2 | delete from account where userflag = '2';
3 |
4 | delete from bindcard where userflag = '2';
--------------------------------------------------------------------------------
/src/main/resources/iiaccount_db.properties:
--------------------------------------------------------------------------------
1 | #数据库配置
2 | DB_Name=${DB_Name}
3 | DB_Password=${DB_Password}
4 | DB_IP=${DB_IP}
5 | #数据池配置
6 | DP_Name=${DP_Name}
7 | DP_Password=${DP_Password}
8 | DP_IP=${DP_IP}
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/exceptions/ErrorException.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.exceptions;
2 |
3 | public class ErrorException extends Exception {
4 |
5 | public ErrorException(String msg){
6 | super(msg); //父类构造函数
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/temp/environment.properties:
--------------------------------------------------------------------------------
1 | #Thu Nov 01 17:15:00 CST 2018
2 | DatabaseLoginIP=10.232.132.6\:1521\:p2b
3 | DatabaseLoginName=op_iiaccount
4 | DatabaseLoginPass=op_iiaccount
5 | baseURI=http\://10.232.138.107\:8187/v1/gateway.do
6 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/functioins/Function.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.functioins;
2 |
3 | /*
4 | *自定义函数,比如生成随机数函数、加密函数等
5 | */
6 | public interface Function {
7 |
8 | String excute(String args[]);
9 |
10 | String getReferenceKey();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/filters/filter_sit.properties:
--------------------------------------------------------------------------------
1 | #SIT环境
2 | Environment=sit
3 | baseURI=http://10.10.10.10
4 | basePath=v1/gateway.do
5 | port=8187
6 | #数据库配置oracle
7 | DB_Name=user
8 | DB_Password=pass
9 | DB_IP=10.10.10.10:1521:p2b
10 | #数据池配置mysql
11 | DP_Name=user
12 | DP_Password=user
13 | DP_IP=10.10.10.10:3306/database
--------------------------------------------------------------------------------
/src/main/resources/temp/categories.json:
--------------------------------------------------------------------------------
1 | [{
2 | "name": "Ignored tests",
3 | "matchedStatuses": ["skipped"]
4 | },
5 | {
6 | "name": "Product defects",
7 | "matchedStatuses": ["failed"]
8 | },
9 | {
10 | "name": "Test defects",
11 | "matchedStatuses": ["broken"]
12 | }
13 | ]
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/asserts/ConstVariable.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.asserts;
2 |
3 | /*
4 | *定义公共变量
5 | */
6 | public class ConstVariable {
7 |
8 | //数据库断言 临时xml文件生成路径,根路径为D:\IntelliJ_IDEA_workspace\ApiAutoTest
9 | //environment.properties路径
10 | public static final String xmlOutputPath = ".\\src\\main\\resources\\temp";
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/functioins/PhoneFunction.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.functioins;
2 |
3 | import com.iiaccount.utils.PhoneUtil;
4 |
5 | /*
6 | *生成11位手机号函数
7 | */
8 | public class PhoneFunction implements Function{
9 |
10 | @Override
11 | public String excute(String[] args) {
12 | return PhoneUtil.getPhone(); //返回11位手机号
13 | }
14 |
15 | @Override
16 | public String getReferenceKey() {
17 | return "phone";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/testngXml/testSuite_YG.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/resources/sql/setUpSQL.sql:
--------------------------------------------------------------------------------
1 | --案例执行前参数准备
2 | -- 商户维护,内部商户:2001(正常),2002(正常),2003(正常),2004(正常),2005(失效)
3 | insert when (not exists (select merchant_id from merchant where merchant_id = '2001')) then into merchant
4 | values ('2001', '商户-正常', 1, to_date('06-06-2017', 'dd-mm-yyyy'), to_date('06-06-2017', 'dd-mm-yyyy')) select '2001' from dual;
5 |
6 | insert when (not exists (select merchant_id from merchant where merchant_id = '2001')) then into merchant
7 | values ('2005', '商户-失效', 2, to_date('06-06-2017', 'dd-mm-yyyy'), to_date('06-06-2017', 'dd-mm-yyyy')) select '2001' from dual;
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/asserts/ContainAssert.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.asserts;
2 |
3 | import org.testng.Assert;
4 |
5 | /*
6 | *判断预期结果是否包含在响应报文中
7 | */
8 | public class ContainAssert {
9 |
10 | public static boolean contains(String source, String search) {
11 | return source.contains(search);
12 | /*
13 | Assert.assertTrue(source.contains(search),
14 | String.format("期待'%s'包含'%s',实际为不包含.", source, search));
15 | */
16 | }
17 |
18 | public static void notContains(String source, String search) {
19 | Assert.assertFalse(source.contains(search),
20 | String.format("期待'%s'不包含'%s',实际为包含.", source, search));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 |
8 | public class FileUtil {
9 |
10 | /*
11 | *移动文件位置
12 | */
13 | public static void copyFiles(File fromFile, File toFile) throws IOException {
14 | FileInputStream ins = new FileInputStream(fromFile);
15 | FileOutputStream out = new FileOutputStream(toFile);
16 | byte[] b = new byte[1024];
17 | int n=0;
18 | while((n=ins.read(b))!=-1){
19 | out.write(b, 0, n);
20 | }
21 |
22 | ins.close();
23 | out.close();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/GainSqlUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.util.Map;
4 | import java.util.Set;
5 |
6 | public class GainSqlUtil {
7 |
8 | /*
9 | *拼装sql语句
10 | */
11 | public static String gainSqlFromMap(String tableName,
12 | Map map) {
13 | String sql = "select * from " + tableName + " where 1=1 ";
14 | Set set = map.keySet();
15 |
16 | for (String keyName : set) {
17 | String value = map.get(keyName);
18 | if (value != null) {
19 | sql = sql + " and " + keyName + " = " + "'" + value + "'";
20 | }
21 | }
22 | return sql;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/StringUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | public class StringUtil {
4 |
5 | /**
6 | *
7 | * @param sourceStr 待替换字符串
8 | * @param matchStr 匹配字符串
9 | * @param replaceStr 目标替换字符串
10 | * @return
11 | */
12 | public static String replaceFirst(String sourceStr,String matchStr,String replaceStr){
13 | int index = sourceStr.indexOf(matchStr);
14 | int matLength = matchStr.length();
15 | int sourLength = sourceStr.length();
16 | String beginStr = sourceStr.substring(0,index);
17 | String endStr = sourceStr.substring(index+matLength,sourLength);
18 | sourceStr = beginStr+replaceStr+endStr;
19 | return sourceStr;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/listener/FailedRetry.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.listener;
2 |
3 | import com.iiaccount.exceptions.ErrorException;
4 | import org.testng.IRetryAnalyzer;
5 | import org.testng.ITestResult;
6 |
7 | /*
8 | *案例执行抛异常则重试
9 | */
10 | public class FailedRetry implements IRetryAnalyzer {
11 | private int retryCount = 1;
12 | private static final int maxRetryCount = 2;
13 |
14 | public boolean retry(ITestResult iTestResult) {
15 | //抛出异常则重跑失败案例
16 | if (iTestResult.getThrowable() instanceof ErrorException && retryCount % maxRetryCount != 0) {
17 | retryCount++;
18 | return true;
19 | } else {
20 | retryCount = 1;
21 | return false;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/PhoneUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | public class PhoneUtil {
4 |
5 | private static String[] telFirst="134,135,136,137,138,139,150,151,152,157,158,159,130,131,132,155,156,133,153,185,186".split(",");
6 |
7 | public static int getNum(int start,int end) {
8 | return (int)(Math.random()*(end-start+1)+start);
9 | }
10 |
11 | /**
12 | * 返回手机号码
13 | */
14 | public static String getPhone() {
15 | int index=getNum(0,telFirst.length-1);
16 | String first=telFirst[index];
17 | String second=String.valueOf(getNum(1,888)+10000).substring(1);
18 | String third=String.valueOf(getNum(1,9100)+10000).substring(1);
19 | return first+second+third;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/functioins/RandomFunction.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.functioins;
2 |
3 | import com.iiaccount.utils.RandomUtil;
4 |
5 | /*
6 | *生成随机数函数,默认为6位
7 | */
8 | public class RandomFunction implements Function {
9 |
10 | @Override
11 | public String excute(String[] args) {
12 | int len = args.length;
13 | int length = 6;// 默认为6
14 | boolean flag = false;// 默认为false,true为纯数字
15 | if (len > 0) {// 第一个参数字符串长度
16 | length = Integer.valueOf(args[0]);
17 | }
18 | if (len > 1) {// 第二个参数是否纯字符串
19 | flag = Boolean.valueOf(args[1]);
20 | }
21 | return RandomUtil.getRandom(length, flag);
22 | }
23 |
24 | @Override
25 | public String getReferenceKey() {
26 | return "random";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/resources/databaseAssert/OpenYg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ACCOUNT_NO
7 | CUST_ID
8 | MERCHANT_ID
9 | 1
10 |
11 |
12 |
13 |
14 |
15 | ACCOUNT_NO
16 | CUST_ID
17 | MERCHANT_ID
18 | 2
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/IsNumericUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.math.BigDecimal;
4 | import java.util.regex.Matcher;
5 | import java.util.regex.Pattern;
6 |
7 | public class IsNumericUtil {
8 |
9 | /*
10 | * 判断送入的字符串是否为数值(包括正数,负数,含小数位的数) //add by lrb 20170927
11 | */
12 | public static boolean isNumeric(String str) {
13 | // 该正则表达式可以匹配所有的数字 包括负数
14 | Pattern pattern = Pattern.compile("-?[0-9]+\\.?[0-9]*");
15 | String bigStr;
16 | try {
17 | bigStr = new BigDecimal(str).toString();
18 | } catch (Exception e) {
19 | return false;//异常 说明包含非数字。
20 | }
21 |
22 | Matcher isNum = pattern.matcher(bigStr); // matcher是全匹配
23 | if (!isNum.matches()) {
24 | return false;
25 | }
26 | return true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/functioins/IdNoFunction.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.functioins;
2 |
3 | import com.iiaccount.utils.RegisterLocationUtil;
4 |
5 | import static com.iiaccount.utils.IdNoUtil.*;
6 |
7 | /*
8 | *生成18位身份证号码函数
9 | */
10 | public class IdNoFunction implements Function {
11 | @Override
12 | public String excute(String[] args) {
13 | StringBuffer strBuffer = new StringBuffer();
14 | strBuffer.append(randomLocationCode(RegisterLocationUtil.registerLocation()));
15 | strBuffer.append(randomBirthday());
16 | strBuffer.append(randomCode());
17 | String eighteenth = verificationCode(strBuffer.toString());
18 | strBuffer.append(eighteenth);
19 | return strBuffer.toString(); //返回18位身份证号
20 | }
21 |
22 | @Override
23 | public String getReferenceKey() {
24 | return "idno";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/listener/FailedRetryListener.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.listener;
2 |
3 | import org.testng.IAnnotationTransformer;
4 | import org.testng.IRetryAnalyzer;
5 | import org.testng.annotations.ITestAnnotation;
6 |
7 | import java.lang.reflect.Constructor;
8 | import java.lang.reflect.Field;
9 | import java.lang.reflect.Method;
10 | import java.lang.reflect.Parameter;
11 |
12 | /*
13 | *实现IAnnotationTransformer接口,修改@Test的retryAnalyzer属性
14 | */
15 | public class FailedRetryListener implements IAnnotationTransformer {
16 | public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
17 | {
18 | IRetryAnalyzer retry = iTestAnnotation.getRetryAnalyzer();
19 | if (retry == null) {
20 | iTestAnnotation.setRetryAnalyzer(FailedRetry.class);
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/dao/ExcutSqlFile.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.dao;
2 |
3 | import com.iiaccount.utils.GetFileMess;
4 | import org.apache.ibatis.io.Resources;
5 | import org.apache.ibatis.jdbc.ScriptRunner;
6 | import org.testng.annotations.Test;
7 |
8 | import java.io.IOException;
9 | import java.nio.charset.Charset;
10 | import java.sql.Connection;
11 | import java.sql.SQLException;
12 |
13 | //执行sql文件语句
14 | public class ExcutSqlFile {
15 |
16 | public static void excute(String fileName) throws SQLException, IOException, ClassNotFoundException {
17 | Connection conn = DBDPConnection.getDBConnection();
18 | ScriptRunner runner = new ScriptRunner(conn);
19 | Resources.setCharset(Charset.forName("UTF-8"));
20 | runner.setDelimiter(";"); //语句结束符号设置
21 | runner.setLogWriter(null);//设置是否输出日志
22 |
23 | //案例执行前的参数维护
24 | runner.runScript(Resources.getResourceAsReader("./sql/"+ fileName +".sql"));
25 | runner.closeConnection();
26 | conn.close();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/RandomUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.util.Random;
4 |
5 | public class RandomUtil {
6 |
7 | public static String randomBase = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";//字母和数字
8 | public static String randomNumberBase = "0123456789";
9 |
10 | public static Random random = new Random();
11 |
12 | public static String getRandom(int length, boolean onlyNumber) {
13 | String base;
14 | if (onlyNumber) {
15 | base = randomNumberBase;
16 | } else {
17 | base = randomBase;
18 | }
19 |
20 | StringBuilder sb = new StringBuilder();
21 | for (int i = 0; i < length; i++) {
22 | char chr;
23 | do {
24 | int number = random.nextInt(base.length());
25 | chr = base.charAt(number);
26 | } while (i==0&&chr=='0') ;//第一个字符不能为0,
27 |
28 | sb.append(chr);
29 | }
30 | return sb.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/java/com/iiaccout/yiguan/SendmsgYg.java:
--------------------------------------------------------------------------------
1 | package com.iiaccout.yiguan;
2 |
3 | import com.iiaccount.common.RunCaseJson;
4 | import com.iiaccount.common.SetUpTearDown;
5 | import com.iiaccount.data.DataProviders;
6 | import io.qameta.allure.Feature;
7 | import io.qameta.allure.Story;
8 | import io.restassured.response.Response;
9 | import org.testng.annotations.Test;
10 |
11 | import java.io.IOException;
12 | import java.sql.SQLException;
13 |
14 | import static com.iiaccount.asserts.Asserts.asserts;
15 |
16 | /*
17 | *一贯短信发送接口
18 | * 环境参数在SetUpTearDown 父类定义
19 | */
20 | @Feature("分类账户改造")
21 | public class SendmsgYg extends SetUpTearDown {
22 |
23 | @Story("发送短信")
24 | @Test(dataProvider = "dataprovider", dataProviderClass = DataProviders.class,
25 | description = "发送短信")
26 | public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {
27 |
28 | //发送请求
29 | Response response = RunCaseJson.runCase(bodyString, "post");
30 |
31 | //只进行响应报文断言
32 | asserts(caseMess, bodyString, response.asString(), "", null);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=INFO, stdout, D , E
2 |
3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
5 | log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %C.%M(%L) ] - [ %p ] %m%n
6 |
7 | # 文件达到指定大小的时候产生一个新的文件
8 | log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
9 | # TODO 部署时,修改为指定路径,done
10 | log4j.appender.D.File=D:/tools/ApiAutoTesting/logs/apiAutoTest_debug.log
11 | log4j.appender.D.Append = true
12 | # 输出DEBUG级别以上的日志
13 | log4j.appender.D.Threshold = DEBUG
14 | log4j.appender.D.layout=org.apache.log4j.PatternLayout
15 | log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %C.%M(%L) ] - [ %p ] %m%n
16 |
17 | ### 保存异常信息到单独文件 ###
18 | log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
19 | ## 异常日志文件名
20 | # TODO 部署时,修改为指定路径,done
21 | log4j.appender.E.File = D:/tools/ApiAutoTesting/logs/apiAutoTest_error.log
22 | log4j.appender.E.Append = true
23 | ## 只输出ERROR级别以上的日志!!!
24 | log4j.appender.E.Threshold = ERROR
25 | log4j.appender.E.layout = org.apache.log4j.PatternLayout
26 | log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %C.%M(%L) ] - [ %p ] %m%n
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/common/RunCaseJson.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.common;
2 |
3 | import io.restassured.RestAssured;
4 | import io.restassured.path.json.JsonPath;
5 | import io.restassured.response.Response;
6 | import ru.yandex.qatools.allure.annotations.Step;
7 |
8 | import static io.restassured.RestAssured.given;
9 |
10 | public class RunCaseJson {
11 |
12 | /*
13 | *post或get方式请求,返回响应报文(json格式)
14 | *@bodyString:json格式的请求报文体
15 | *@para:requestType post或get
16 | */
17 | public static Response runCase(String bodyString,String requestType){
18 | Response response = null;
19 | if(requestType.toLowerCase().equals("get"))
20 | response = given()
21 | .contentType("application/json;charset=UTF-8")
22 | .request()
23 | .body(bodyString)
24 | .get();
25 | else
26 | response = given()
27 | .contentType("application/json;charset=UTF-8")
28 | .request()
29 | .body(bodyString)
30 | .post();
31 |
32 | //打印格式化的参数
33 | //response.prettyPrint(); ////去掉部分日志 add by lrb 20181029
34 |
35 | return response;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/data/DataProviders.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.data;
2 |
3 | import com.iiaccount.utils.AssembledMessForJson;
4 | import jxl.read.biff.BiffException;
5 | import org.testng.annotations.DataProvider;
6 |
7 | import java.io.IOException;
8 | import java.lang.reflect.Method;
9 | import java.net.URISyntaxException;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | public class DataProviders {
14 |
15 | /*
16 | *map包含两部分json,key为caseNo等信息,value为接口入参
17 | */
18 | @DataProvider(name = "dataprovider",parallel = true)
19 | public static Object[][] dataP(Method method) throws IOException, BiffException, URISyntaxException {
20 | String className = method.getDeclaringClass().getSimpleName(); //获取类名
21 | String caseFileName = className+".xls"; //测试案例名称为:类名.xls
22 |
23 | Object[][] objects = null;
24 | Map map = new HashMap();
25 | map = AssembledMessForJson.assembleMess(caseFileName,""); //""表示读取所有的为Y的case
26 | objects = new Object[map.size()][2];
27 | int i=0;
28 | for(Map.Entry entry : map.entrySet()){
29 | objects[i][0] = entry.getKey();
30 | objects[i][1] = entry.getValue();
31 | i++;
32 | }
33 | map.clear(); //需清空map,否则案例会不断叠加 2018-10-19 add by lrb
34 | return objects;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/FunctionUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import com.iiaccount.functioins.Function;
4 |
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | public class FunctionUtil {
10 |
11 | //所有继承Function的类
12 | private static final Map> functionsMap = new HashMap>();
13 | static {
14 | List> clazzes = ClassFinder.getAllAssignedClass(Function.class);
15 | clazzes.forEach((clazz) -> {
16 | try {
17 | // function
18 | Function tempFunc = (Function) clazz.newInstance();
19 | String referenceKey = tempFunc.getReferenceKey();
20 |
21 | if (referenceKey.length() > 0) { // ignore self
22 | functionsMap.put(referenceKey, tempFunc.getClass());
23 | }
24 | } catch (Exception ex) {
25 | ex.printStackTrace();
26 | }
27 | });
28 | }
29 |
30 | public static boolean isFunction(String functionName){
31 | return functionsMap.containsKey(functionName);
32 | }
33 |
34 | public static String getValue(String functionName,String[] args){
35 | try {
36 | return functionsMap.get(functionName).newInstance().excute(args);
37 | } catch (Exception e) {
38 | e.printStackTrace();
39 | return "";
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ApiAutoTest
2 | 框架介绍详见文章:[基于TestNG+Rest Assured+Allure的接口自动化测试框架](https://www.jianshu.com/p/880f5eeba016)
3 | # 约定规则
4 | - 数据库使用的是oracle,数据池使用mysql
5 | - 部署时根据需要配置日志输出路径,log4j.properties
6 | - 案例执行前依赖参数在sql/setUpSQL.sql维护
7 | - 案例执行后清理数据在sql/tearDownSQL.sql维护
8 | - resources/testCase及databaseAssert目录下的文件名与test测试类类名保持一致
9 | - 接口公共入参字段在resources/PublicArgs.properties文件配置
10 | - 自定义函数(后续根据需要扩展即可)
11 | 1、__random(var1,var2),生成var1长度的随机数
12 | 2、__phone():生成11位的手机号
13 | 3、__idno():生成18位身份证号
14 | - 预期结果断言支持json格式及包含函数(不区分大小写)可结合使用。
15 | 示例:$.status=200;$.data.accountNo=6230780501000877514;__contAIN(SUCCESS)
16 | - 目前暂只支持post,get方式的请求。
17 | - 案例抛异常则自动重试;
18 | - 对于无需数据库断言的接口,参考发SendmsgYg.java例子
19 | - 对于需要数据库断言的接口,则需将数据库断言xml的变量添加到map,参考OpenYg.java例子
20 | - 接口测试类需继承SetUpTearDown类。
21 | 示例:public class SendmsgYg extends SetUpTearDown {}
22 | - 目前提供以下五种供数方式,通过以下方式使用。
23 | 1、查询数据池供数:${dp.sql(select accountNo from account where status = 1)}
24 | 2、查询数据库供数:${db.sql(select accountNo from account_card where status = 1)}
25 | 3、先接口请求,然后提取响应报文供数:${SendmsgYg.case023.post($.data.code)} 或 ${SendmsgYg.case023.get($.data.code)}
26 | 4、先接口请求,然后查询数据库供数:${SendmsgYg.case023.post.db.sql(select accountNo from M_account_card where status = 1)}
27 | 5、自定义函数
28 | - 数据库及数据池操作通过以下方法来访问:
29 | 数据池查询:CRUDData.selectData(sql,"DP"),返回多个值的话只取第一个
30 | 数据池新增修改删除:CRUDData.cUDData(sql,"DP")
31 | 数据库查询:CRUDData.selectData(sql,"DB"),返回多个值的话只取第一个
32 | 数据库新增修改删除:CRUDData.cUDData(sql,"DB")
33 | - 其他功能后续可根据需要扩展,比如文件上传、加密、解密等。
34 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/report/TestStep.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.report;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import io.qameta.allure.Attachment;
5 |
6 | /*
7 | *测试步骤,测试报告中展现
8 | */
9 | public class TestStep {
10 |
11 | public static void requestAndRespondBody(String URL, String Body,String Respond){
12 | requestBody(URL,Body);
13 | respondBody(Respond);
14 | }
15 |
16 | @Attachment("请求报文")
17 | public static String requestBody(String URL, String body) {
18 |
19 | //格式化json串
20 | boolean prettyFormat = true; //格式化输出
21 | JSONObject jsonObject = JSONObject.parseObject(body);
22 | String str = JSONObject.toJSONString(jsonObject,prettyFormat);
23 |
24 | //报告展现请求报文
25 | return URL+"\n"+str;
26 | }
27 |
28 | @Attachment("响应报文")
29 | public static String respondBody(String respond) {
30 |
31 | //格式化json串
32 | boolean prettyFormat = true; //格式化输出
33 | JSONObject jsonObject = JSONObject.parseObject(respond);
34 | String str = JSONObject.toJSONString(jsonObject,prettyFormat);
35 |
36 | //报告展现响应报文
37 | return str;
38 | }
39 |
40 | @Attachment("数据库断言结果")
41 | public static StringBuffer databaseAssertResult(StringBuffer assertResult){
42 | //报告展现数据库断言结果
43 | return assertResult;
44 | }
45 |
46 | @Attachment("响应报文断言结果")
47 | public static StringBuffer assertRespond(StringBuffer assertResult){
48 | //报告展现数据库断言结果
49 | return assertResult;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/GetFileMess.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import io.restassured.path.json.JsonPath;
4 | import org.apache.log4j.Logger;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.net.URL;
9 | import java.util.Properties;
10 |
11 | /*
12 | *读取文件的信息
13 | */
14 | public class GetFileMess {
15 | private static Properties properties;
16 | Logger log = Logger.getLogger(GetFileMess.class);
17 |
18 | /*
19 | *根据properties文件主键获取对应的值
20 | */
21 | public String getValue(String key,String propertiesFileName) throws IOException {
22 | InputStream stream = this.getClass().getClassLoader().getResourceAsStream("\\"+propertiesFileName);
23 | properties = new Properties();
24 | properties.load(stream);
25 | String value = properties.getProperty(key);
26 | return value;
27 | }
28 |
29 | /*
30 | *获取文件路径
31 | */
32 | public String getFilePath(String directory,String fileName) {
33 |
34 | try{
35 | URL resource = this.getClass().getClassLoader().getResource(directory+"/"+fileName);
36 | String filePath = resource.toURI().getPath();
37 | log.info("filePath:"+filePath);
38 | return filePath;
39 | }catch (Exception e){
40 | log.info(e.getMessage());
41 | return null;
42 | }
43 | }
44 |
45 | /*
46 | *获取测试案例数据的预期结果
47 | */
48 | public String getCaseMessKeyValue(String caseMess,String key){
49 | JsonPath caseMessToJson = new JsonPath(caseMess);
50 | String value = caseMessToJson.get(key);
51 | return value;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/dao/CRUDData.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.dao;
2 |
3 | import org.apache.log4j.Logger;
4 |
5 | import java.io.IOException;
6 | import java.sql.*;
7 |
8 | public class CRUDData {
9 | static Logger log = Logger.getLogger(CRUDData.class);
10 |
11 | static Connection connection = null;
12 | static ResultSet result = null;
13 |
14 | /*
15 | * 查询
16 | * 只针对select a from talbe 类型的语句;
17 | * select * from 或 select a,b from等暂时用不到,暂不支持;
18 | */
19 | public static String selectData(String sql,String flag) throws SQLException, IOException, ClassNotFoundException {
20 | String value = "";
21 | if(flag.toLowerCase().equals("db"))
22 | connection = DBDPConnection.getDBConnection();
23 | else
24 | connection = DBDPConnection.getDPConnection();
25 |
26 | Statement statement = connection.createStatement();
27 | result = statement.executeQuery(sql);
28 |
29 | if (result.next()) { //如果存在多个值,只取第一个
30 | value = result.getString(1);
31 | }
32 | log.info(sql+"取值为:"+value);
33 |
34 | if (result != null)
35 | result.close();
36 | if (connection != null)
37 | connection.close();
38 |
39 | return value;
40 | }
41 |
42 | //新增,修改,删除
43 | public static void cUDData(String sql,String flag) throws SQLException, IOException, ClassNotFoundException {
44 |
45 | if(flag.toLowerCase().equals("db"))
46 | connection = DBDPConnection.getDBConnection();
47 | else
48 | connection = DBDPConnection.getDPConnection();
49 |
50 | PreparedStatement preparedStatement = connection.prepareStatement(sql);
51 | preparedStatement.executeUpdate();
52 |
53 | log.info("新增/修改/删除语句:"+sql);
54 |
55 | if (connection != null){
56 | preparedStatement.close();
57 | connection.close();
58 | }
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/dao/DBDPConnection.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.dao;
2 |
3 | import com.iiaccount.utils.GetFileMess;
4 | import org.apache.log4j.Logger;
5 |
6 | import java.io.IOException;
7 | import java.sql.Connection;
8 | import java.sql.DriverManager;
9 | import java.sql.SQLException;
10 |
11 | public class DBDPConnection {
12 |
13 | static Logger log = Logger.getLogger(DBDPConnection.class);
14 |
15 | static Connection dBConnection = null; //数据库链接 oracle
16 | static Connection dPConnection = null; //数据池链接 mysql
17 |
18 | //数据库连接,oracle
19 | public static Connection getDBConnection() throws ClassNotFoundException, IOException, SQLException {
20 | String database = "iiaccount_db.properties"; //通过filter配置数据库环境
21 | String url = new GetFileMess().getValue("DB_IP",database);
22 | String user = new GetFileMess().getValue("DB_Name",database);
23 | String password = new GetFileMess().getValue("DB_Password",database);
24 |
25 | Class.forName("oracle.jdbc.driver.OracleDriver");
26 | url = "jdbc:oracle:thin:@" + url;
27 | log.info("数据库:"+url+"|"+user+"|"+password);
28 | dBConnection = DriverManager.getConnection(url, user, password);
29 | return dBConnection;
30 | }
31 |
32 | //数据池连接,mysql
33 | public static Connection getDPConnection() throws IOException, ClassNotFoundException, SQLException {
34 | String database = "iiaccount_db.properties"; //通过filter配置数据库环境
35 | String url = new GetFileMess().getValue("DP_IP",database);
36 | String user = new GetFileMess().getValue("DP_Name",database);
37 | String password = new GetFileMess().getValue("DP_Password",database);
38 |
39 | // 加载驱动程序
40 | Class.forName("com.mysql.jdbc.Driver");
41 | url = "jdbc:mysql://" + url +"?characterEncoding=gb2312&serverTimezone=UTC"; //ip配置;
42 | log.info("数据池:"+url+"|"+user+"|"+password);
43 | dPConnection = DriverManager.getConnection(url, user, password);
44 | return dPConnection;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/com/iiaccout/yiguan/OpenYg.java:
--------------------------------------------------------------------------------
1 | package com.iiaccout.yiguan;
2 |
3 | import com.iiaccount.asserts.RespondAssertForJson;
4 | import com.iiaccount.common.RunCaseJson;
5 | import com.iiaccount.common.SetUpTearDown;
6 | import com.iiaccount.dao.CRUDData;
7 | import com.iiaccount.data.DataProviders;
8 | import io.qameta.allure.Feature;
9 | import io.qameta.allure.Story;
10 | import io.restassured.response.Response;
11 | import org.testng.annotations.Test;
12 |
13 | import java.io.IOException;
14 | import java.sql.SQLException;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import static com.iiaccount.asserts.Asserts.asserts;
19 |
20 | /*
21 | *一贯开立II/III类户
22 | * 环境参数在SetUpTearDown 父类定义
23 | */
24 | @Feature("分类账户改造")
25 | public class OpenYg extends SetUpTearDown {
26 |
27 | @Story("分类账户开户")
28 | @Test(dataProvider = "dataprovider", dataProviderClass = DataProviders.class
29 | , description = "开户")
30 | public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {
31 |
32 | //发送请求
33 | Response response = RunCaseJson.runCase(bodyString, "post");
34 |
35 | //如果需要数据库断言,此处添加断言文件变量的map映射
36 | //可通过调用封装的方法取值,比如查数据库、提取响应报文、调用接口等方式。
37 | Map map = new HashMap<>();
38 | //查询数据库获取,取不到值返回""
39 | String account = CRUDData.selectData("select accountNo from ACCOUNT where status =1","DB");
40 | //提取响应报文,取不到值返回NULL
41 | String custId = RespondAssertForJson.getBuildValue(response.asString(),"$.data.custid");
42 | //执行SendmsgYg接口的case023案例,然后提取响应报文的merchanId ,取不到值返回NULL
43 | String merchanId = RespondAssertForJson.getBuildValue("","${SendmsgYg.case023.post($.data.merchanId)}");
44 |
45 | map.put("ACCOUNT_NO", account);
46 | map.put("CUST_ID", custId);
47 | map.put("MERCHANT_ID", merchanId);
48 |
49 | //断言(包含响应报文断言和数据库断言)
50 | String xmlFileName = this.getClass().getSimpleName(); //数据库断言xml文件名(与类名保持一致)
51 | asserts(caseMess, bodyString, response.asString(), xmlFileName, map);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/WritePropertiesUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import com.iiaccount.asserts.ConstVariable;
4 |
5 | import java.io.*;
6 | import java.util.Iterator;
7 | import java.util.Map;
8 | import java.util.Properties;
9 |
10 | import static com.iiaccount.utils.FileUtil.copyFiles;
11 |
12 | //写Properties文件
13 | public class WritePropertiesUtil {
14 |
15 | public static void writePropertiesFile(Map data) throws IOException {
16 |
17 | String filename = "environment.properties";
18 | String filename1 = "categories.json";
19 | // 生成xml文件
20 | File file = new File(ConstVariable.xmlOutputPath + "\\" + filename); //temp目录下
21 |
22 | // 判断是否存在,如果不存在,则创建
23 | if (!file.exists()) {
24 | file.createNewFile();
25 | }
26 | String filepath = file.getAbsolutePath();
27 |
28 | Properties props = new Properties();
29 | InputStream is = WritePropertiesUtil.class.getClassLoader().getResourceAsStream(filepath);
30 | try {
31 | InputStream input = new FileInputStream(filepath);
32 | props.load(input);
33 |
34 | //在保存配置文件之前还需要取得该配置文件的输出流,
35 | if (data != null) {
36 | Iterator> iter = data.entrySet().iterator();
37 | while (iter.hasNext()) {
38 | Map.Entry entry = iter.next();
39 | props.setProperty(entry.getKey(), entry.getValue());
40 | }
41 | }
42 |
43 | OutputStream out = new FileOutputStream(filepath);
44 | props.store(out, null);
45 | input.close();
46 | out.close();
47 |
48 | //拷贝environment.properties和categories.json文件至allure-results路径下
49 | File file1 = new File(ConstVariable.xmlOutputPath + "\\" + filename1);
50 | File toFile = new File(".\\target\\allure-results\\"+filename);
51 | File toFile1 = new File(".\\target\\allure-results\\"+filename1);
52 | copyFiles(file,toFile);
53 | copyFiles(file1,toFile1);
54 |
55 | } catch (IOException e) {
56 | e.printStackTrace();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/common/SetUpTearDown.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.common;
2 |
3 | import com.iiaccount.dao.ExcutSqlFile;
4 | import com.iiaccount.utils.GetFileMess;
5 | import io.restassured.RestAssured;
6 | import org.testng.annotations.AfterSuite;
7 | import org.testng.annotations.BeforeClass;
8 | import org.testng.annotations.BeforeSuite;
9 |
10 | import java.io.IOException;
11 | import java.sql.SQLException;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | import static com.iiaccount.utils.WritePropertiesUtil.writePropertiesFile;
16 |
17 | /*
18 | *父类的注解可以被子类继承,所有的环境变量在父类进行配置
19 | *根据testng.xml文件传入的参数来选择环境参数,默认为yg_env
20 | */
21 | public class SetUpTearDown {
22 |
23 | @BeforeSuite
24 | public void dataSetUp() throws SQLException, IOException, ClassNotFoundException {
25 | ExcutSqlFile.excute("setUpSQL"); //案例执行前参数维护
26 | }
27 |
28 | //环境配置
29 | @BeforeClass
30 | public void envSetUp() {
31 | try {
32 | String system = "env.properties"; //环境由filter配置
33 | RestAssured.baseURI = new GetFileMess().getValue("baseURI", system);
34 | RestAssured.basePath = new GetFileMess().getValue("basePath", system);
35 | RestAssured.port = Integer.parseInt(new GetFileMess().getValue("port", system));
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 | }
40 |
41 | /*
42 | *创建environment.properties并放到allure-results目录下,测试报告展现
43 | */
44 | @AfterSuite
45 | public void createEnvPropertiesForReport() throws IOException {
46 | Map data = new HashMap<>();
47 | String database = "iiaccount_db.properties";
48 | data.put("DatabaseLoginName", new GetFileMess().getValue("DB_Name", database));
49 | data.put("DatabaseLoginPass", new GetFileMess().getValue("DB_Password", database));
50 | data.put("DatabaseLoginIP", new GetFileMess().getValue("DB_IP", database));
51 | data.put("baseURI", RestAssured.baseURI + ":" + RestAssured.port + "/" + RestAssured.basePath);
52 |
53 | writePropertiesFile(data);
54 | }
55 |
56 | @AfterSuite
57 | public void dataTearDown() throws SQLException, IOException, ClassNotFoundException {
58 | //案例执行结束后,对数据池的数据进行清理(删除或更新状态)
59 | ExcutSqlFile.excute("tearDownSQL"); //案例执行后数据清理
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/ClassFinder.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.io.UnsupportedEncodingException;
6 | import java.net.URLDecoder;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class ClassFinder {
11 |
12 | static ClassLoader classloader = Thread.currentThread().getContextClassLoader();
13 | /**
14 | * 获取同一路径下所有子类或接口实现类
15 | *
16 | * @param intf
17 | * @return
18 | * @throws IOException
19 | * @throws ClassNotFoundException
20 | */
21 | public static List> getAllAssignedClass(Class> cls) {
22 | List> classes = new ArrayList>();
23 | for (Class> c : getClasses(cls)) {
24 | if (cls.isAssignableFrom(c) && !cls.equals(c)) {
25 | classes.add(c);
26 | }
27 | }
28 | return classes;
29 | }
30 |
31 | /**
32 | * 取得当前类路径下的所有类
33 | *
34 | * @param cls
35 | * @return
36 | * @throws IOException
37 | * @throws ClassNotFoundException
38 | */
39 | public static List> getClasses(Class> cls) {
40 | String pk = cls.getPackage().getName();
41 | String path = pk.replace('.', '/');
42 | // URL url = classloader.getResource(path);
43 | // return getClasses(new File(url.getFile()), pk);
44 | try {
45 | String dirPath = URLDecoder.decode(classloader.getResource(path).getPath(),"utf-8");
46 | return getClasses(new File(dirPath), pk);
47 | } catch (UnsupportedEncodingException e) {
48 | e.printStackTrace();
49 | }
50 | return new ArrayList>();
51 | }
52 |
53 | /**
54 | * 迭代查找类
55 | *
56 | * @param dir
57 | * @param pk
58 | * @return
59 | * @throws ClassNotFoundException
60 | */
61 | private static List> getClasses(File dir, String pk) {
62 | List> classes = new ArrayList>();
63 | if (!dir.exists()) {
64 | return classes;
65 | }
66 | for (File f : dir.listFiles()) {
67 | if (f.isDirectory()) {
68 | classes.addAll(getClasses(f, pk + "." + f.getName()));
69 | }
70 | String name = f.getName();
71 | if (name.endsWith(".class")) {
72 | try{
73 | classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
74 | }catch(Exception ex){
75 | }
76 | }
77 | }
78 | return classes;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/asserts/Asserts.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.asserts;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.iiaccount.utils.GetFileMess;
5 | import io.restassured.RestAssured;
6 | import org.apache.log4j.Logger;
7 | import org.testng.Assert;
8 |
9 | import java.io.IOException;
10 | import java.sql.SQLException;
11 | import java.util.Map;
12 |
13 | import static com.iiaccount.asserts.DatabaseAssert.verifyDatabase;
14 | import static com.iiaccount.asserts.RespondAssertForJson.verifyResult;
15 | import static com.iiaccount.report.TestStep.assertRespond;
16 | import static com.iiaccount.report.TestStep.databaseAssertResult;
17 | import static com.iiaccount.report.TestStep.requestAndRespondBody;
18 | import static com.iiaccount.utils.SplitXmlForCaseNo.xmlOutTemp;
19 |
20 | /*
21 | *数据库断言及响应报文断言
22 | */
23 | public class Asserts {
24 |
25 | static Logger log = Logger.getLogger(Asserts.class);
26 |
27 | public static void asserts(String caseMess, String bodyString,String response,String xmlFileName,Map map) throws SQLException, IOException, ClassNotFoundException {
28 |
29 |
30 | //测试报告展现请求报文 响应报文
31 | String url = RestAssured.baseURI + ":" + RestAssured.port + "/" + RestAssured.basePath;
32 | requestAndRespondBody(url,bodyString,response);
33 |
34 | String preResult = new GetFileMess().getCaseMessKeyValue(caseMess, "preResult");
35 | String tableCheck = new GetFileMess().getCaseMessKeyValue(caseMess, "tableCheck");
36 | String caseNo = new GetFileMess().getCaseMessKeyValue(caseMess, "caseNo");
37 |
38 | //格式化json串
39 | boolean prettyFormat = true; //格式化输出
40 | JSONObject jsonObject = JSONObject.parseObject(response);
41 |
42 | response = JSONObject.toJSONString(jsonObject,prettyFormat);
43 |
44 | log.info("案例编号:"+ caseNo);
45 | log.info("响应报文:"+response);
46 | //断言(包含响应报文断言和数据库断言)
47 | databaseAndRespondAsserts(response,preResult,tableCheck,xmlFileName, caseNo, map);
48 | }
49 |
50 | public static void databaseAndRespondAsserts(String sourceData, String verifyData, String tableCheck, String fileName, String caseNo, Map map) throws IOException, SQLException, ClassNotFoundException {
51 |
52 | StringBuffer stringBufferResult = new StringBuffer();
53 | StringBuffer stringBufferDatabase = new StringBuffer();
54 | String path = "";
55 | boolean assertFlag = true;
56 |
57 | //响应报文断言
58 | stringBufferResult = verifyResult(sourceData, verifyData);
59 | //测试报告展现 响应报文断言结果(无论成功还是失败)
60 | assertRespond(stringBufferResult);
61 | // 断言不通过,flag标志赋值为false
62 | if (stringBufferResult.indexOf("断言false") != -1) {
63 | assertFlag = false;
64 | }
65 |
66 | //tableCheck为Y/y才进行数据库响应断言
67 | if (tableCheck.toUpperCase().trim().equals("Y")) {
68 | //临时xml文件
69 | path = xmlOutTemp(fileName, caseNo, map);
70 |
71 | if (!path.equals("")) {
72 | stringBufferDatabase = verifyDatabase(caseNo, path);
73 |
74 | //测试报告展现 数据库断言结果(无论成功还是失败)
75 | databaseAssertResult(stringBufferDatabase);
76 |
77 | if (stringBufferDatabase.indexOf("检查不通过") != -1) {
78 | assertFlag = false;
79 | }
80 | }
81 | }
82 |
83 | //断言
84 | Assert.assertTrue(assertFlag, "响应报文断言或数据库断言失败,请查看断言结果!");
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/IdNoUtil.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import java.util.*;
4 |
5 | /*
6 | *参考资料:https://blog.csdn.net/u012898245/article/details/79404745
7 | *参考资料:https://www.cnblogs.com/liuhongfeng/p/4789274.html?tvd
8 | *证件号组成规则
9 | * 户籍所在地(第1到第6位)+ 出生日期(第7到第14位)+ 落户派出所代码(第15、16位)+ 性别代码(第17位)+ 验证码(第18位)
10 | */
11 | public class IdNoUtil {
12 |
13 | /**
14 | * 生成第18位身份证号
15 | *
16 | * @param
17 | * @return 身份证校验码的计算方法
18 | * 将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2。
19 | * 将这17位数字和系数相乘的结果相加。
20 | * 用加出来和除以11,看余数是多少?
21 | * 余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。
22 | * 其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2。
23 | */
24 | public static String verificationCode(String str17) {
25 | char[] chars = str17.toCharArray();
26 | if (chars.length < 17) {
27 | return " ";
28 | }
29 | int[] coefficient = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
30 | char[] resultChar = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
31 | int[] numberArr = new int[17];
32 | int result = 0;
33 | for (int i = 0; i < numberArr.length; i++) {
34 | numberArr[i] = Integer.parseInt(chars[i] + "");
35 | }
36 | for (int i = 0; i < numberArr.length; i++) {
37 | result += coefficient[i] * numberArr[i];
38 | }
39 | return String.valueOf(resultChar[result % 11]);
40 |
41 | }
42 |
43 | /**
44 | * 随机获取落户派出所代码(第15、16位) + 性别代码(第17位)
45 | * 直接生成三位数
46 | *
47 | * @return
48 | */
49 | public static String randomCode() {
50 | int code = (int) (Math.random() * 1000);
51 | if (code < 10) {
52 | return "00" + code;
53 | } else if (code < 100) {
54 | return "0" + code;
55 | } else {
56 | return "" + code;
57 | }
58 | }
59 |
60 | /*
61 | * 随机生成出生日期
62 | * @return
63 | */
64 | public static String randomBirthday() {
65 | Calendar birthday = Calendar.getInstance();
66 | birthday.set(Calendar.YEAR, (int) (Math.random() * 60) + 1950);
67 | birthday.set(Calendar.MONTH, (int) (Math.random() * 12));
68 | birthday.set(Calendar.DATE, (int) (Math.random() * 31));
69 | StringBuilder builder = new StringBuilder();
70 | builder.append(birthday.get(Calendar.YEAR));
71 | long month = birthday.get(Calendar.MONTH) + 1;
72 | if (month < 10) {
73 | builder.append("0");
74 | }
75 | builder.append(month);
76 | long date = birthday.get(Calendar.DATE);
77 | if (date < 10) {
78 | builder.append("0");
79 | }
80 | builder.append(date);
81 | return builder.toString();
82 | }
83 |
84 | /**
85 | * 随机获取区号
86 | *
87 | * @param registerLocation
88 | * @return
89 | */
90 | public static String randomLocationCode(Map registerLocation) {
91 | int index = (int) (Math.random() * registerLocation.size());
92 | Collection values = registerLocation.values();
93 | Iterator it = values.iterator();
94 | int i = 0;
95 | int locationCode = 0;
96 | while (i <= index && it.hasNext()) {
97 | i++;
98 | if (i == index) {
99 | locationCode = it.next();
100 | }
101 | }
102 | return String.valueOf(locationCode);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/AssembledMessForJson.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import com.google.gson.Gson;
4 | import com.iiaccount.asserts.RespondAssertForJson;
5 | import jxl.Sheet;
6 | import jxl.Workbook;
7 | import jxl.read.biff.BiffException;
8 | import org.apache.log4j.Logger;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /*
16 | *根据测试用例文件拼装报文body(json格式)
17 | * 输入一贯改造_短信发送.xls,return json串列表
18 | */
19 | public class AssembledMessForJson {
20 |
21 | static Logger log = Logger.getLogger(AssembledMessForJson.class);
22 | static Map bodyMap = new HashMap();
23 | static Map dataMap = new HashMap();
24 | static Map caseMessMap = new HashMap();
25 | static Map map = new HashMap();
26 |
27 | /*
28 | @Test()
29 | public void test() throws IOException, BiffException, URISyntaxException {
30 | assembleMess("sendmsgYg.xls","case022");
31 | }
32 | */
33 |
34 |
35 | /*
36 | *入参为testCase目录下的案例数据文件名
37 | */
38 | public static Map assembleMess(String fileName,String caseNo) throws IOException, BiffException {
39 | log.info("文件名:" + fileName);
40 |
41 | String filePath = new GetFileMess().getFilePath("testCase", fileName);
42 | File xlsFile = new File(filePath);
43 | Workbook workbook = Workbook.getWorkbook(xlsFile); // 获得工作簿对象
44 | Sheet sheet = workbook.getSheet(0); // 获得工作表
45 | int rows = sheet.getRows(); // 获得行数
46 | int cols = sheet.getColumns(); // 获得列数
47 |
48 | String pubArgs = new GetFileMess().getValue("pubArgsYg", "PublicArgs.properties");
49 | log.info("接口公共入参pubArgsYg:" + pubArgs);
50 |
51 | bodyMap.clear();
52 | dataMap.clear();
53 | caseMessMap.clear();
54 |
55 | for (int row = 1; row < rows; row++) {
56 | String yOn = sheet.getCell(3, row).getContents();
57 | String caseNo1 = sheet.getCell(0, row).getContents().toLowerCase(); //文件中的案例编号
58 | if (yOn.equals("Y") && caseNo.equals("")) { //获取全部为Y的案例报文体
59 | getMap(sheet, cols, row, pubArgs);
60 | } else if (caseNo1.equals(caseNo.toLowerCase())) { //获取单条案例报文体,不管执行标识是否为Y
61 | getMap(sheet, cols, row, pubArgs);
62 | }
63 | }
64 |
65 | workbook.close();
66 | return map;
67 | }
68 |
69 | public static void getMap(Sheet sheet, int cols, int row, String pubArgs){
70 |
71 | for (int col = 0; col < cols; col++) {
72 |
73 | String cellKey = sheet.getCell(col, 0).getContents();//表头
74 | String cellValue = sheet.getCell(col, row).getContents();//值
75 | if (col >= 5) {
76 | //appid,api,version属于公共入参,公共入参字段在PublicArgs.properties文件进行配置
77 | // getBuildValue(value1,value2)方法用于转换${}或者函数为对应的值
78 | if (pubArgs.toLowerCase().contains(cellKey.toLowerCase().trim())) {
79 | bodyMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
80 | } else {
81 | dataMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
82 | }
83 | } else {
84 | caseMessMap.put(cellKey, cellValue);
85 | }
86 | }
87 | bodyMap.put("data", dataMap);
88 | map.put(new Gson().toJson(caseMessMap), new Gson().toJson(bodyMap));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/data/DataBuilders.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.data;
2 |
3 | import com.iiaccount.asserts.RespondAssertForJson;
4 | import com.iiaccount.common.RunCaseJson;
5 | import com.iiaccount.dao.CRUDData;
6 | import com.iiaccount.utils.AssembledMessForJson;
7 | import io.restassured.response.Response;
8 | import jxl.read.biff.BiffException;
9 | import org.apache.log4j.Logger;
10 |
11 | import java.io.IOException;
12 | import java.sql.SQLException;
13 | import java.util.regex.Matcher;
14 | import java.util.regex.Pattern;
15 |
16 | //数据提供
17 | public class DataBuilders {
18 |
19 | static Logger log = Logger.getLogger(DataBuilders.class);
20 |
21 | /*
22 | *目前提供以下四种供数方式:
23 | * -查询数据池供数:${dp.sql(select accountNo from M_account where status = 1)}
24 | 表达式:(dp.sql)\((.*)\)
25 | * -查询数据库供数:${db.sql(select accountNo from M_account_card where status = 1)}
26 | 表达式:(db.sql)\((.*)\)
27 | * -先接口请求,然后提取响应报文供数:${SendmsgYg.case023.post($.data.code)} 或${SendmsgYg.case023.get($.data.code)}
28 | 表达式:(.*case.*)\((.*)\)
29 | * -先接口请求,然后查询数据库/池供数:${SendmsgYg.case023.post.db.sql(select accountNo from M_account_card where status = 1)}
30 | 表达式:(.*case.*).db.sql\((.*)\)
31 | 表达式:(.*case.*).dp.sql\((.*)\) --暂时不用
32 | */
33 | protected static Pattern dataPoolPattern = Pattern.compile("(dp.sql)\\((.*)\\)");
34 | protected static Pattern databasePattern = Pattern.compile("(db.sql)\\((.*)\\)");
35 | protected static Pattern reponsePattern = Pattern.compile("(.*case.*).post\\((.*)\\)");
36 | protected static Pattern httpDataPoolPattern = Pattern.compile("(.*case.*).dp.sql\\((.*)\\)");
37 | protected static Pattern httpDdatabasePattern = Pattern.compile("(.*case.*).db.sql\\((.*)\\)");
38 |
39 | public static String dataprovide(String var) throws SQLException, IOException, ClassNotFoundException, BiffException {
40 | String value = "";
41 | Matcher dpMatch = dataPoolPattern.matcher(var);
42 | Matcher dbMatch = databasePattern.matcher(var);
43 | Matcher responseMatch = reponsePattern.matcher(var);
44 | Matcher httpDbMatch = httpDdatabasePattern.matcher(var);
45 | Matcher httpDpMatch = httpDataPoolPattern.matcher(var);
46 |
47 |
48 | if(dpMatch.find()){//查询数据池供数
49 | String sql = dbMatch.group(2);
50 | value = CRUDData.selectData(sql,"DP");
51 | log.info("查询数据池供数:"+ value);
52 | }else if(dbMatch.find()){//查询数据库供数
53 | String sql = dbMatch.group(2);
54 | value = CRUDData.selectData(sql,"DB");
55 | log.info("查询数据库供数:"+ value);
56 | }else if(responseMatch.find()){//先接口请求,然后提取响应报文供数
57 | String jsonPath = responseMatch.group(2); //$.data.code
58 | String response = runCase(responseMatch);
59 |
60 | //根据$.data.code获取响应报文中对应的值
61 | value = RespondAssertForJson.getBuildValue(response, jsonPath);
62 |
63 | }else if(httpDbMatch.find()){//先接口请求,然后查询数据库供数
64 | String sql = httpDbMatch.group(2); //select accountNo from M_account_card where status = 1
65 | runCase(httpDbMatch);
66 |
67 | //根据sql语句获取相应的值
68 | value = CRUDData.selectData(sql,"DB");
69 |
70 | }else if(httpDpMatch.find()){//先接口请求,然后查询数据池供数(由于接口请求后数据不入数据池,此场景可以用于只执行依赖接口)
71 | String sql = httpDpMatch.group(2); //select accountNo from M_account_card where status = 1
72 | runCase(httpDpMatch);
73 |
74 | //根据sql语句获取相应的值
75 | value = CRUDData.selectData(sql,"DP");
76 | }
77 |
78 | return value;
79 | }
80 |
81 | //根据caseNo进行接口请求
82 | public static String runCase(Matcher matcher) throws IOException, BiffException {
83 |
84 | String[] caseMess = matcher.group(1).split("."); //SendmsgYg.case023.post
85 | String fileName = caseMess[0]+".xls"; //案例文件名
86 | String caseNo = caseMess[1]; //案例编号
87 | String requestType = caseMess[2]; //请求类型post/get
88 | //String sql = matcher.group(2); //select accountNo from M_account_card where status = 1
89 | String bodyString = "";
90 | //该map只有一条记录
91 | for(String value1:AssembledMessForJson.assembleMess(fileName,caseNo).values()){
92 | bodyString = value1;
93 | }
94 |
95 | log.info("matcher.group(1):"+matcher.group(1));
96 | log.info("matcher.group(2):"+matcher.group(2));
97 | log.info("bodyString:"+bodyString);
98 |
99 | //发送请求
100 | Response response = RunCaseJson.runCase(bodyString,requestType);
101 |
102 | return response.asString();
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/asserts/RespondAssertForJson.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.asserts;
2 |
3 | import com.alibaba.fastjson.JSONPath;
4 | import com.iiaccount.data.DataBuilders;
5 | import com.iiaccount.utils.FunctionUtil;
6 | import com.iiaccount.utils.StringUtil;
7 | import org.apache.log4j.Logger;
8 |
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | public class RespondAssertForJson {
13 |
14 | static Logger log = Logger.getLogger(RespondAssertForJson.class);
15 |
16 | /**
17 | * 替换符,如果数据中包含“${}”则会被替换成公共参数中存储的数据
18 | */
19 | protected static Pattern replaceParamPattern = Pattern.compile("\\$\\{(.*?)\\}");
20 |
21 | /**
22 | * 截取自定义函数正则表达式:__random(value1,value2)
23 | */
24 | protected static Pattern funPattern = Pattern
25 | .compile("__(\\w*?)\\((([\\w]*,?)*)\\)");
26 |
27 | /*
28 | *包含断言的正则表达式,__contain(sssss)
29 | */
30 | protected static Pattern containPattern = Pattern.compile("__(contain)\\(.+\\)");
31 |
32 | /*
33 | *对预期结果断言,json断言和contain断言允许在同一预期结果中使用,比如$.status=200;__contain(tomandy)
34 | *无论断言成功还是失败,测试报告展现断言结果
35 | */
36 | public static StringBuffer verifyResult(String sourceData, String verifyData) {
37 | if (verifyData.equals("") || verifyData == null)
38 | return null;
39 |
40 | log.info("待验证的预期结果为:" + verifyData);
41 |
42 | boolean assertFlag = true;
43 | StringBuffer stringBuffer = new StringBuffer();
44 |
45 | String assertStr[] = verifyData.split(";");
46 | for (String assertString : assertStr) {
47 |
48 | if (assertString.toLowerCase().contains("__contain(")) {
49 | // 验证包含断言
50 | log.info("contain断言表达式:" + assertString);
51 | //提取__contain()函数里的字符串
52 | String containMess = assertString.substring(10, assertString.length() - 1);
53 | assertFlag = ContainAssert.contains(sourceData, containMess);
54 | if (!assertFlag)
55 | stringBuffer.append("【" + assertString + "断言" + assertFlag + String.format(",期待\n'%s'\n包含'%s',实际不包含!】\n", sourceData, containMess));
56 | else
57 | stringBuffer.append("【" + assertString + "断言" + assertFlag + String.format(",期待\n'%s'\n包含'%s',实际包含!】\n", sourceData, containMess));
58 | } else if (assertString.toLowerCase().contains("$.")) {
59 | log.info("json断言表达式:" + assertString);
60 | //json断言,通过;隔开
61 | Pattern pattern = Pattern.compile("([^;]*)=([^;]*)");
62 | Matcher matcher = pattern.matcher(assertString.trim());
63 | while (matcher.find()) {
64 | //根据$.status的json格式匹配响应报文中的值
65 | String actualValue = getBuildValue(sourceData, matcher.group(1));
66 | log.info("matcher.group(1):" + matcher.group(1));
67 |
68 | //假如预期结果为$.status=200,则匹配值200,200也可以替换为自定义函数
69 | String exceptValue = getBuildValue(sourceData, matcher.group(2));
70 | log.info("matcher.group(2):" + matcher.group(2));
71 |
72 | log.info(String.format("验证转换后的值%s=%s", actualValue,
73 | exceptValue));
74 |
75 | //如果有多个断言,前面断言的失败,不会再校验后面的断言
76 | //Assert.assertEquals(actualValue, exceptValue, "验证预期结果失败!");
77 | //无论断言成功还是失败,都保存断言
78 | if (exceptValue.equals(actualValue)) {
79 | assertFlag = true;
80 | stringBuffer.append("【" + matcher.group() + "断言" + assertFlag + String.format(",期待预期结果为'%s',实际结果为'%s'!】\n", exceptValue, actualValue));
81 | } else {
82 | assertFlag = false;
83 | stringBuffer.append("【" + matcher.group() + "断言" + assertFlag + String.format(",期待预期结果为'%s',实际结果为'%s'!】\n", exceptValue, actualValue));
84 | }
85 | }
86 | } else {
87 | //Assert.assertTrue(false, "【预期结果断言格式有误,目前仅支持Json及contain断言,多个断言使用英文分号隔开,例如:$.status=200;__contain(tomandy)】");
88 | assertFlag = false;
89 | stringBuffer.append("【预期结果断言" + assertFlag + ",断言格式有误,目前仅支持Json及contain断言,多个断言使用英文分号隔开,例如:$.status=200;__contain(tomandy)】\n");
90 | }
91 | }
92 | return stringBuffer;
93 | }
94 |
95 | /**
96 | * 支持json串转换
97 | * 支持自定义函数的转换
98 | * 支持${}变量转换
99 | *
100 | * @param sourchJson
101 | * @param key
102 | * @return
103 | */
104 | public static String getBuildValue(String sourchJson, String key) {
105 | key = key.trim();
106 | Matcher funMatch = funPattern.matcher(key);
107 | Matcher replacePattern = replaceParamPattern.matcher(key);
108 |
109 | //log.info("key is:" + key); //去掉部分日志 add by lrb 20181029
110 | try{
111 | if (key.startsWith("$.")) {// jsonpath
112 | key = JSONPath.read(sourchJson, key).toString(); //jsonpath读取对应的值
113 | log.info("key start with $.,value is:" + key);
114 | } else if (funMatch.find()) {//函数
115 |
116 | String args = funMatch.group(2); //函数入参
117 | log.info("key is a function,args is:" + args);
118 | String[] argArr = args.split(",");
119 | for (int index = 0; index < argArr.length; index++) {
120 | String arg = argArr[index];
121 | if (arg.startsWith("$.")) { //函数入参亦支持json格式
122 | argArr[index] = JSONPath.read(sourchJson, arg).toString();
123 | }
124 | }
125 | log.info("argArr:"+argArr.length);
126 | String value = FunctionUtil.getValue(funMatch.group(1), argArr); //函数名不区分大小写,返回函数值
127 | log.info("函数名 funMatch.group(1):" + funMatch.group(1));
128 | key = StringUtil.replaceFirst(key, funMatch.group(), value); //把函数替换为生成的值
129 | log.info("函数 funMatch.group():" + funMatch.group());
130 | log.info("key is a function,value is:" + key);
131 | } else if (replacePattern.find()) {//${}变量
132 | log.info("${}变量体:"+replacePattern.group(1));
133 | String var = replacePattern.group(1).trim();
134 |
135 | String value1 = DataBuilders.dataprovide(var);
136 | key = StringUtil.replaceFirst(key, replacePattern.group(), value1); //把变量替换为生成的值
137 | log.info("key is a ${} pattern,value is:" + key);
138 | }
139 | return key;
140 |
141 | }catch(Exception e){
142 |
143 | log.info(e.getMessage());
144 | return null;
145 | }
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | HFIIACCOUNT
8 | ApiAutoTest
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | 1.8.10
14 |
15 | UTF-8
16 |
17 |
18 | testSuite_YG.xml
19 |
20 |
21 |
22 |
23 | org.testng
24 | testng
25 | 6.11
26 |
27 |
28 |
29 | io.rest-assured
30 | rest-assured
31 | 3.1.0
32 |
33 |
34 |
35 | ru.yandex.qatools.allure
36 | allure-testng-adaptor
37 | 1.3.6
38 |
39 |
40 | org.testng
41 | testng
42 |
43 |
44 |
45 |
46 |
47 | io.qameta.allure
48 | allure-testng
49 | 2.0-BETA14
50 |
51 |
52 |
53 | net.sourceforge.jexcelapi
54 | jxl
55 | 2.6.12
56 |
57 |
58 |
59 | com.google.code.gson
60 | gson
61 | 2.8.2
62 |
63 |
64 |
65 | com.alibaba
66 | fastjson
67 | [1.2.31,)
68 |
69 |
70 |
71 | com.oracle
72 | ojdbc14
73 | 10.2.0.4.0
74 |
75 |
76 |
77 | org.mybatis
78 | mybatis
79 | 3.4.4
80 |
81 |
82 |
83 | mysql
84 | mysql-connector-java
85 | 6.0.6
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | src/main/resources
94 | true
95 |
96 |
97 |
98 |
99 | src/main/filters/filter_${env}.properties
100 |
101 |
102 |
103 |
104 | org.apache.maven.plugins
105 | maven-surefire-plugin
106 | 2.20
107 |
108 |
109 | -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
110 |
111 |
112 |
113 |
114 | allure.results.directory
115 | ./target/allure-results
116 |
117 |
118 |
119 |
120 |
121 | org.aspectj
122 | aspectjweaver
123 | ${aspectj.version}
124 |
125 |
126 |
127 |
128 |
129 | org.apache.maven.plugins
130 | maven-surefire-plugin
131 | 2.19
132 |
133 |
134 |
135 |
136 | ${project.basedir}/target/classes/testngXml/${xmlFileName}
137 |
138 |
139 |
140 |
141 |
142 |
143 | org.apache.maven.plugins
144 | maven-compiler-plugin
145 |
146 | 8
147 | 8
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | org.apache.maven.plugins
156 | maven-resources-plugin
157 | 2.6
158 |
159 |
160 |
161 | xls
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | uat
174 |
175 | uat
176 |
177 |
178 |
179 |
180 |
181 |
182 | sit
183 |
184 | sit
185 |
186 |
187 | true
188 |
189 |
190 |
191 |
192 |
193 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/utils/SplitXmlForCaseNo.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.utils;
2 |
3 | import com.iiaccount.asserts.ConstVariable;
4 | import org.apache.log4j.Logger;
5 | import org.w3c.dom.Document;
6 | import org.w3c.dom.Element;
7 | import org.w3c.dom.Node;
8 | import org.w3c.dom.NodeList;
9 |
10 | import javax.xml.parsers.DocumentBuilder;
11 | import javax.xml.parsers.DocumentBuilderFactory;
12 | import javax.xml.transform.Transformer;
13 | import javax.xml.transform.TransformerFactory;
14 | import javax.xml.transform.dom.DOMSource;
15 | import javax.xml.transform.stream.StreamResult;
16 | import java.io.File;
17 | import java.io.FileOutputStream;
18 | import java.util.Map;
19 |
20 | /*
21 | *将databaseAssert目录下的数据库断言按caseNo拆分为临时xml文件
22 | */
23 | public class SplitXmlForCaseNo {
24 |
25 | static Logger log = Logger.getLogger(SplitXmlForCaseNo.class) ;
26 |
27 | /*
28 | @Test
29 | public void test() throws IOException {
30 | String fileName = "OpenYg";
31 | String caseNo = "case085";
32 | Map map = new HashMap<>();
33 |
34 | xmlOutTemp(fileName,caseNo,map);
35 | }
36 | */
37 |
38 | /*
39 | * 读取包含全部case的xml文件,区分caseNo生成临时的xml文件,并替换xml文件中的变量
40 | * @para fileName:文件名,约定跟类名保持一致
41 | * @para caseNo:案例编号
42 | * @para map:xml文件的变量映射
43 | */
44 | public static String xmlOutTemp(String fileName, String caseNo,
45 | Map map) { // 区分case生成临时文件
46 |
47 | String table_name = "";
48 | String filePathIn = new GetFileMess().getFilePath("databaseAssert",fileName+".xml");
49 | String filePathOut = "";
50 |
51 | if(filePathIn != null){
52 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
53 | try {
54 | DocumentBuilder db = dbf.newDocumentBuilder();
55 |
56 | Document doc = db.parse(new File(filePathIn)); // 读取xml文件
57 |
58 | NodeList caseList = doc.getElementsByTagName("caseNo");
59 |
60 | log.info("共有" + caseList.getLength() + "个caseNo节点");
61 |
62 | for (int i = 0; i < caseList.getLength(); i++) {
63 | Node case_node = caseList.item(i); // 第一个caseNo节点
64 |
65 | Element elem0 = (Element) case_node; // caseNo节点对象
66 | String caseNo_name = elem0.getAttribute("case_no");
67 |
68 | log.info("案例编号:" + caseNo_name);
69 |
70 | if (caseNo_name.equals(caseNo)) { // 根据caseNo入参选择性遍历
71 |
72 | // -----------------------------------------------------------------------------
73 | // 操作的Document对象
74 | Document document = db.newDocument();
75 | // 设置XML的版本
76 | document.setXmlVersion("1.0");
77 | // 创建根节点
78 | Element root = document.createElement("caseNo");
79 | root.setAttribute("case_no", caseNo);
80 | // 将根节点添加到Document对象中
81 | document.appendChild(root);
82 | // ------------------------------------------------------------------------------
83 |
84 | for (Node table_node = case_node.getFirstChild(); table_node != null; table_node = table_node
85 | .getNextSibling()) {
86 |
87 | if (table_node.getNodeType() == Node.ELEMENT_NODE) // 如果当前节点为元素节点
88 | {
89 | Element elem1 = (Element) table_node; // table节点对象
90 | table_name = elem1.getAttribute("table_name"); // 获取表名
91 | log.info("表名:" + table_name);
92 | }
93 | // ------------------------------------------------------------------------------
94 | Element tableElement = document.createElement("table");
95 | // 设置page节点的name属性
96 | tableElement.setAttribute("table_name", table_name);
97 | root.appendChild(tableElement);
98 | // ------------------------------------------------------------------------------
99 |
100 | for (Node column_node = table_node.getFirstChild(); column_node != null; column_node = column_node
101 | .getNextSibling()) {
102 | if (column_node.getNodeType() == Node.ELEMENT_NODE) // 如果当前节点为元素节点
103 | {
104 | Element elem2 = (Element) column_node; // column节点对象
105 | String priKey_name = elem2
106 | .getAttribute("key_name"); // 获取主键名
107 | String column_name = elem2
108 | .getAttribute("column_name"); // 获取检查字段名
109 |
110 | String value = "";
111 | if(column_node.getFirstChild() != null){ //可能会存在 这类节点,导致取getNodeValue抛异常
112 | value = column_node.getFirstChild().getNodeValue(); //获取值
113 | }else{
114 | value = "";
115 | }
116 |
117 | if (!priKey_name.equals("")) {
118 |
119 | log.info("主键名:" + priKey_name + " 值:" + value);
120 | // ------------------------------------------------------------------------------
121 | Element priKeyElement = document
122 | .createElement("priKey");
123 | // 设置page节点的name属性
124 | priKeyElement.setAttribute("key_name",
125 | priKey_name);
126 | priKeyElement.setTextContent(value);
127 |
128 | for (String keyName : map.keySet()) { // 遍历map,替换变量
129 | if (value.equals(keyName)) { // xml的变量存在map中,则使用map的值
130 | priKeyElement.setTextContent(map
131 | .get(keyName));
132 | break;
133 | }
134 | }
135 | tableElement.appendChild(priKeyElement);
136 | // ------------------------------------------------------------------------------
137 | }
138 |
139 | if (!column_name.equals("")) {
140 |
141 | log.info("列名:" + column_name + " 值:" + value);
142 | // ------------------------------------------------------------------------------
143 | Element columnElement = document
144 | .createElement("column");
145 | // 设置page节点的name属性
146 | columnElement.setAttribute("column_name",
147 | column_name);
148 | columnElement.setTextContent(value);
149 |
150 | log.info("map:" + map);
151 | for (String keyName : map.keySet()) { // 遍历map,替换变量
152 | if (value.equals(keyName)) { // xml的变量存在map中,则使用map的值
153 | columnElement.setTextContent(map
154 | .get(keyName));
155 | break;
156 | }
157 | }
158 | tableElement.appendChild(columnElement);
159 | // ------------------------------------------------------------------------------
160 | }
161 | }
162 | }
163 | table_name = "";
164 | }
165 | // ------------------------------------------------------------------------------
166 | // 开始把Document映射到文件
167 | TransformerFactory transFactory = TransformerFactory
168 | .newInstance();
169 | Transformer transFormer = transFactory.newTransformer();
170 | // 设置输出结果
171 | DOMSource domSource = new DOMSource(document);
172 | // 生成xml文件
173 | File file = new File(ConstVariable.xmlOutputPath +"\\" +fileName + "_temp.xml"); //temp目录下
174 |
175 | // 判断是否存在,如果不存在,则创建
176 | if (!file.exists()) {
177 | file.createNewFile();
178 | }
179 | // 文件输出流
180 | FileOutputStream out = new FileOutputStream(file);
181 | // 设置输入源
182 | StreamResult xmlResult = new StreamResult(out);
183 | // 输出xml文件
184 | transFormer.transform(domSource, xmlResult);
185 | // 测试文件输出的路径
186 | log.info("数据库断言临时xml文件路径:"+file.getAbsolutePath());
187 |
188 | filePathOut = file.getAbsolutePath();
189 | // ------------------------------------------------------------------------------
190 | }
191 | }
192 | } catch (Exception e) {
193 | log.info(e.getMessage());
194 | e.printStackTrace();
195 | return "";
196 | }
197 | }
198 |
199 | return filePathOut;
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/main/java/com/iiaccount/asserts/DatabaseAssert.java:
--------------------------------------------------------------------------------
1 | package com.iiaccount.asserts;
2 |
3 | import com.iiaccount.dao.DBDPConnection;
4 | import org.apache.log4j.Logger;
5 | import org.w3c.dom.Document;
6 | import org.w3c.dom.Element;
7 | import org.w3c.dom.Node;
8 | import org.w3c.dom.NodeList;
9 |
10 | import javax.xml.parsers.DocumentBuilder;
11 | import javax.xml.parsers.DocumentBuilderFactory;
12 | import java.io.File;
13 | import java.io.IOException;
14 | import java.sql.*;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import static com.iiaccount.utils.GainSqlUtil.gainSqlFromMap;
19 | import static com.iiaccount.utils.IsNumericUtil.isNumeric;
20 |
21 | public class DatabaseAssert {
22 |
23 | static Logger log = Logger.getLogger(DatabaseAssert.class);
24 |
25 | private static Connection connection = null;
26 |
27 | /*
28 | * 读取临时xml文件,根据xml的检查点进行数据库检查,并返回检查结果
29 | */
30 | public static StringBuffer verifyDatabase(String caseNo, String filePath) throws IOException, ClassNotFoundException, SQLException { //返回检查不通过的原因
31 |
32 | StringBuffer excuteResultStr = new StringBuffer();
33 | String flagResultStr = "";
34 | String sql = "";
35 | String table_name = "";
36 | Map keyMap = new HashMap();
37 | Map columnMap = new HashMap();
38 |
39 | // add by lrb 20180921 每检查一张表都需要链接一次数据库,性能损耗大,把connect放到外面
40 | connection = DBDPConnection.getDBConnection();
41 |
42 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
43 | try {
44 | DocumentBuilder db = dbf.newDocumentBuilder();
45 | Document doc = db.parse(new File(filePath));
46 |
47 | NodeList caseList = doc.getElementsByTagName("caseNo");
48 |
49 | for (int i = 0; i < caseList.getLength(); i++) {
50 | Node case_node = caseList.item(i); // 第一个caseNo节点
51 |
52 | Element elem0 = (Element) case_node; // caseNo节点对象
53 | String caseNo_name = elem0.getAttribute("case_no");
54 | log.info("案例编号:" + caseNo_name);
55 |
56 | if (caseNo_name.equals(caseNo)) { // 根据caseNo入参选择性遍历
57 | for (Node table_node = case_node.getFirstChild(); table_node != null; table_node = table_node
58 | .getNextSibling()) {
59 |
60 | if (table_node.getNodeType() == Node.ELEMENT_NODE) // 如果当前节点为元素节点
61 | {
62 |
63 | Element elem1 = (Element) table_node; // table节点对象
64 | table_name = elem1.getAttribute("table_name"); // 获取表名
65 | log.info("表名:" + table_name);
66 | }
67 |
68 | for (Node column_node = table_node.getFirstChild(); column_node != null; column_node = column_node
69 | .getNextSibling()) {
70 | if (column_node.getNodeType() == Node.ELEMENT_NODE) // 如果当前节点为元素节点
71 | {
72 | Element elem2 = (Element) column_node; // column节点对象
73 | String priKey_name = elem2.getAttribute("key_name"); // 获取主键名
74 | String column_name = elem2.getAttribute("column_name"); // 获取检查字段名
75 |
76 | String value = "";
77 | if (column_node.getFirstChild() != null) { //可能会存在 这类节点,导致取getNodeValue抛异常
78 | value = column_node.getFirstChild().getNodeValue(); //获取值
79 | } else {
80 | value = "";
81 | }
82 |
83 | if (!priKey_name.equals("")) {
84 | keyMap.put(priKey_name, value);
85 | log.info("主键名:" + priKey_name + " 值:" + value);
86 | }
87 |
88 | if (!column_name.equals("")) {
89 | columnMap.put(column_name, value);
90 | log.info("列名:" + column_name + " 值:" + value);
91 | }
92 |
93 | }
94 | }
95 | if (keyMap.size() != 0 && columnMap.size() != 0) {
96 | log.info("keyMap: " + keyMap);
97 | log.info("columnMap: " + columnMap);
98 |
99 | sql = gainSqlFromMap(table_name, keyMap); //根据索引拼装sql
100 | log.info("sql: " + sql);
101 |
102 | flagResultStr = checkTable(sql, columnMap); //数据库检查
103 |
104 | String strFlag[] = flagResultStr.split("&&&&%%%%%%@@@@");// 分割字符串得到数组
105 |
106 | if (strFlag[0].equals("false")) {
107 | log.info("检查结果:" + table_name + "检查不通过!");
108 | excuteResultStr.append("【" + table_name + "表检查不通过】\n" + strFlag[1]);
109 | } else {
110 | excuteResultStr.append("【" + table_name + "表检查通过】\n" + strFlag[1]);
111 | }
112 | keyMap.clear();
113 | columnMap.clear();
114 | }
115 | }
116 | }
117 | }
118 |
119 | // add by lrb 20180921 每检查一张表都需要链接一次数据库,性能损耗大,把connect放到外面
120 | if (connection != null)
121 | connection.close();
122 |
123 | } catch (Exception e) {
124 | e.printStackTrace();
125 | excuteResultStr.append("XML文件有误:" + e.getMessage());
126 | return excuteResultStr;
127 | }
128 | return excuteResultStr; //返回检查结果
129 | }
130 |
131 | /*
132 | *
133 | */
134 | public static String checkTable(String sql, Map map) throws ClassNotFoundException,
135 | IOException {
136 |
137 | StringBuffer resultStr = new StringBuffer();
138 | StringBuffer flagStr = new StringBuffer();
139 |
140 | Map mapType = new HashMap();
141 |
142 | // add by lrb 20180921 每检查一张表都需要链接一次数据库,性能损耗大,把connect放到外面
143 | //Connection connection = null;
144 | ResultSet result = null;
145 | boolean flag = true;
146 |
147 | try {
148 | // add by lrb 20180921 每检查一张表都需要链接一次数据库,性能损耗大,把connect放到外面
149 | //url = "jdbc:oracle:thin:@"+url;
150 | //Class.forName("oracle.jdbc.driver.OracleDriver");
151 | //connection = DriverManager.getConnection(url, user, password);// 地址,用户名,密码
152 |
153 | result = connection.prepareStatement(sql).executeQuery();
154 |
155 | ResultSetMetaData metadata = result.getMetaData(); // add by lrb
156 | // 20170927
157 | // 获取每一列的类型NUMBER,VERCHA2,DATE等
158 | for (int i = 1; i <= metadata.getColumnCount(); i++) { // add by lrb
159 | // 20170927
160 | mapType.put(metadata.getColumnName(i),
161 | metadata.getColumnTypeName(i)); // add by lrb 20170927
162 | }
163 |
164 | log.info(mapType);
165 |
166 | if (result.next()) { // 只有一笔记录,无需while,只取第一笔记录
167 |
168 | for (String key : map.keySet()) {
169 | flag = true; // 循环过后需初始化,防止下一次循环取了上一次的结果;
170 |
171 | log.info(key
172 | + "预期结果为:" + map.get(key) + ";实际结果为:"
173 | + result.getString(key));
174 |
175 | if (result.getString(key) != null) //数据库获取的值不为null
176 | {
177 | if (!result.getString(key).trim().equals(map.get(key).trim()) && !map.get(key).toUpperCase().trim().equals("NOTNULL")) { // 预期结果和实际结果不匹配
178 |
179 | flag = false;
180 |
181 | /* map映射的值是字符串,对于金额11.80映射为11.80,但数据库查询返回的是11.8,导致比对是检查不通过;需做特殊处理;
182 | * 如果列类型是NUMBER类型且为数字,则转换为Double类型再进行比较;
183 | * add by lrb 20170927 mapType.get(key).equals("NUMBER")
184 | */
185 | if (mapType.get(key).equals("NUMBER")
186 | && isNumeric(result.getString(key).trim())
187 | && isNumeric(map.get(key).trim())) {// 如果为数值,再进行判断
188 | if (Double.parseDouble(result.getString(key).trim()) == Double
189 | .parseDouble(map.get(key).trim())) // 字符串转换为数值再比较
190 | flag = true;
191 | }
192 |
193 | log.info("flag1:" + flag);
194 |
195 | }
196 |
197 | if (map.get(key).toUpperCase().trim().equals("NOTNULL")) {//只做非空校验
198 | if (result.getString(key).trim().equals("") || result.getString(key) == null) {
199 | flag = false;
200 | }
201 | }
202 | } else {
203 | if (!map.get(key).equals("")) //如果数据库查回来的是null,但上送的预期结果不是空值,则当成校验不通过
204 | flag = false;
205 | }
206 |
207 | log.info("最终的flag:" + flag);
208 |
209 | // 对表的每个字段校验都输出预期和实际结果;//add by linrb 20171031
210 | if (!map.get(key).toUpperCase().trim().equals("NOTNULL")) {
211 | resultStr.append("<" + key + "字段>预期结果为:" + map.get(key)
212 | + ",实际结果为:" + result.getString(key) + "\n");
213 | } else {
214 | resultStr.append("<" + key + "字段>只做非空校验" + ",实际结果为:" + result.getString(key) + "\n");
215 | }
216 |
217 |
218 | // if(flag == false) //有一个值检查不通过,则认定该表校验不过,退出循环 //add by
219 | // linrb 20171031
220 | // break; //add by linrb 20171031
221 |
222 | // 此处需把每次循环的flag标志存起来,防止出现字段1检查不通过、字段2检查通过类型的场景,导致flag不准确;
223 | flagStr.append(flag);
224 |
225 | }
226 | // 如果flagStr含有false,则表示有字段检查不通过,flag标志赋值为false
227 | if (flagStr.indexOf("false") != -1) {
228 | flag = false;
229 | } else {
230 | flag = true;
231 | }
232 |
233 | resultStr.append("\n");
234 |
235 | } else {
236 | log.info("找不到记录");
237 | resultStr.append("找不到记录!" + "\n"); // add by linrb 20171031
238 | flag = false;
239 | }
240 | if (result != null)
241 | result.close();
242 | // add by lrb 20180921 减少数据库请求次数,把connect放到外面
243 | //if (connection != null)
244 | // connection.close();
245 |
246 | } catch (SQLException e) {
247 | flag = false; // 查询数据库抛异常,认为检查不通过;
248 | resultStr.append("比对数据库异常!" + "\n"); // add by linrb 20171031
249 | e.printStackTrace();
250 | }
251 | return flag + "&&&&%%%%%%@@@@" + resultStr; // 比对结果不通过则返回false
252 | }
253 | }
--------------------------------------------------------------------------------