【七:(测试用例)spring boot+testng+xml+mock实现用例管理+数据校验】

目录

  • 1、目录结构的相关类
    • cases类
      • 1、添加用户 AddUserTest
      • 2、获取用户列表信息 GetUserInfoListTest
      • 3、获取用户信息 GetUserInfoTest
      • 4、登录测试
      • 5、更新用户信息
    • config类
      • 1、报告配置
      • 2、用户路径配置
    • model类
    • utils类
  • 配置配置类
      • SQLMapper.xml
      • spring boot全局配置
      • databaseConfig.xml
      • testng.xml配置
  • 测试
    • 1、前提条件
      • 第一步:首先mock数据
      • 第二步:启动服务
      • 第三步::使用postman测试mock服务是否可用
      • 第四步:数据的准备 准备用户名等于 abc的数(用于登录结果的验证)
    • 2、已登录成功案例测试为例:
      • 2.1、直接在用例里面测试验证是否能用
      • 2.2、在xml测试套件是否可用

image.png

1、目录结构的相关类

cases类

1、添加用户 AddUserTest


import com.tester.config.TestConfig;
import com.tester.model.AddUserCase;
import com.tester.model.User;
import com.tester.utils.DatabaseUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;public class AddUserTest {//需要依赖loginTrue的分组的用例@Test(dependsOnGroups = "loginTrue",description = "添加用户接口接口")public void addUser() throws IOException, InterruptedException {SqlSession session = DatabaseUtil.getSqlSession();AddUserCase addUserCase = session.selectOne("addUserCase",1);System.out.println("addUserCase"+addUserCase.toString());System.out.println("addUserUrl"+TestConfig.addUserUrl);/**//下边的代码为写完接口的测试代码String result = getResult(addUserCase);//查询用户看是否添加成功Thread.sleep(2000);User user = session.selectOne("addUser",addUserCase);System.out.println(user.toString());//处理结果,就是判断返回结果是否符合预期Assert.assertEquals(addUserCase.getExpected(),result);*/}private String getResult(AddUserCase addUserCase) throws IOException {//下边的代码为写完接口的测试代码HttpPost post = new HttpPost(TestConfig.addUserUrl);JSONObject param = new JSONObject();param.put("userName",addUserCase.getUserName());param.put("password",addUserCase.getPassword());param.put("sex",addUserCase.getSex());param.put("age",addUserCase.getAge());param.put("permission",addUserCase.getPermission());param.put("isDelete",addUserCase.getIsDelete());//设置请求头信息 设置headerpost.setHeader("content-type","application/json");//将参数信息添加到方法中StringEntity entity = new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//设置cookiesTestConfig.defaultHttpClient.setCookieStore(TestConfig.store);//声明一个对象来进行响应结果的存储String result;//执行post方法HttpResponse response = TestConfig.defaultHttpClient.execute(post);//获取响应结果result = EntityUtils.toString(response.getEntity(),"utf-8");System.out.println(result);return result;}}

2、获取用户列表信息 GetUserInfoListTest


import com.tester.config.TestConfig;
import com.tester.model.GetUserListCase;
import com.tester.model.User;
import com.tester.utils.DatabaseUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONArray;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.Test;import java.io.IOException;
import java.util.List;public class GetUserInfoListTest {@Test(dependsOnGroups="loginTrue",description = "获取性别为男的用户信息")public void getUserListInfo() throws IOException, InterruptedException {SqlSession session = DatabaseUtil.getSqlSession();GetUserListCase getUserListCase = session.selectOne("getUserListCase",1);System.out.println(getUserListCase.toString());System.out.println(TestConfig.getUserListUrl);/**//下边为写完接口的代码  发送请求获取结果JSONArray resultJson = getJsonResult(getUserListCase);Thread.sleep(2000);List<User> userList = session.selectList(getUserListCase.getExpected(),getUserListCase);for(User u : userList){System.out.println("list获取的user:"+u.toString());}JSONArray userListJson = new JSONArray(userList);Assert.assertEquals(userListJson.length(),resultJson.length());for(int i = 0;i<resultJson.length();i++){JSONObject expect = (JSONObject) resultJson.get(i);JSONObject actual = (JSONObject) userListJson.get(i);Assert.assertEquals(expect.toString(), actual.toString());}*/}private JSONArray getJsonResult(GetUserListCase getUserListCase) throws IOException {HttpPost post = new HttpPost(TestConfig.getUserListUrl);JSONObject param = new JSONObject();param.put("userName",getUserListCase.getUserName());param.put("sex",getUserListCase.getSex());param.put("age",getUserListCase.getAge());//设置请求头信息 设置headerpost.setHeader("content-type","application/json");//将参数信息添加到方法中StringEntity entity = new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//设置cookiesTestConfig.defaultHttpClient.setCookieStore(TestConfig.store);//声明一个对象来进行响应结果的存储String result;//执行post方法HttpResponse response = TestConfig.defaultHttpClient.execute(post);//获取响应结果result = EntityUtils.toString(response.getEntity(),"utf-8");JSONArray jsonArray = new JSONArray(result);System.out.println("调用接口list result:"+result);return jsonArray;}}

3、获取用户信息 GetUserInfoTest


import com.tester.config.TestConfig;
import com.tester.model.GetUserInfoCase;
import com.tester.model.User;
import com.tester.utils.DatabaseUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONArray;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class GetUserInfoTest {@Test(dependsOnGroups="loginTrue",description = "获取userId为1的用户信息")public void getUserInfo() throws IOException, InterruptedException {SqlSession session = DatabaseUtil.getSqlSession();GetUserInfoCase getUserInfoCase = session.selectOne("getUserInfoCase",1);System.out.println(getUserInfoCase.toString());System.out.println(TestConfig.getUserInfoUrl);/**//下边为写完接口的代码JSONArray resultJson = getJsonResult(getUserInfoCase);Thread.sleep(2000);User user = session.selectOne(getUserInfoCase.getExpected(),getUserInfoCase);System.out.println("自己查库获取用户信息:"+user.toString());List userList = new ArrayList();userList.add(user);JSONArray jsonArray = new JSONArray(userList);System.out.println("获取用户信息:"+jsonArray.toString());System.out.println("调用接口获取用户信息:"+resultJson.toString());Assert.assertEquals(jsonArray,resultJson);*/}private JSONArray getJsonResult(GetUserInfoCase getUserInfoCase) throws IOException {HttpPost post = new HttpPost(TestConfig.getUserInfoUrl);JSONObject param = new JSONObject();param.put("id",getUserInfoCase.getUserId());//设置请求头信息 设置headerpost.setHeader("content-type","application/json");//将参数信息添加到方法中StringEntity entity = new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//设置cookiesTestConfig.defaultHttpClient.setCookieStore(TestConfig.store);//声明一个对象来进行响应结果的存储String result;//执行post方法HttpResponse response = TestConfig.defaultHttpClient.execute(post);//获取响应结果result = EntityUtils.toString(response.getEntity(),"utf-8");System.out.println("调用接口result:"+result);List resultList = Arrays.asList(result);JSONArray array = new JSONArray(resultList);System.out.println(array.toString());return array;}
}

4、登录测试


import com.tester.model.InterfaceName;
import com.tester.config.TestConfig;
import com.tester.model.LoginCase;
import com.tester.utils.ConfigFile;
import com.tester.utils.DatabaseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;public class LoginTest {@BeforeTest(groups = "loginTrue",description = "测试准备工作,获取HttpClient对象")public void beforeTest(){//声明http客户端TestConfig.defaultHttpClient = new DefaultHttpClient();TestConfig.getUserInfoUrl = ConfigFile.getUrl(InterfaceName.GETUSERINFO);TestConfig.getUserListUrl = ConfigFile.getUrl(InterfaceName.GETUSERLIST);TestConfig.loginUrl = ConfigFile.getUrl(InterfaceName.LOGIN);TestConfig.updateUserInfoUrl = ConfigFile.getUrl(InterfaceName.UPDATEUSERINFO);TestConfig.addUserUrl = ConfigFile.getUrl(InterfaceName.ADDUSERINFO);}@Test(groups = "loginTrue",description = "用户成功登陆接口")public void loginTrue() throws IOException {//查询数据SqlSession session = DatabaseUtil.getSqlSession();LoginCase loginCase = session.selectOne("loginCase",1);//System.out.println("数据库数据:"+loginCase.toString());//验证输出urlSystem.out.println("访问的url:"+TestConfig.loginUrl);//发起http://localhost:8889/login请求,获取结果String result = getResult(loginCase);//处理结果,就是判断返回结果是否符合预期    getExpected=逾期结果   result=实际结果Assert.assertEquals(loginCase.getExpected(),result);System.out.println("预期结果:"+loginCase.getExpected()+"\n"+"实际结果:"+result);}@Test(description = "用户登陆失败接口")public void loginFalse() throws IOException {SqlSession session = DatabaseUtil.getSqlSession();LoginCase loginCase = session.selectOne("loginCase",2);System.out.println(loginCase.toString());System.out.println(TestConfig.loginUrl);/**//下边的代码为写完接口的测试代码String result = getResult(loginCase);//处理结果,就是判断返回结果是否符合预期Assert.assertEquals(loginCase.getExpected(),result);*/}//登录用户private String getResult(LoginCase loginCase) throws IOException {//声明一个对象来进行响应结果的存储String result;//下边的代码为写完接口的测试代码HttpPost post = new HttpPost(TestConfig.loginUrl);JSONObject param = new JSONObject();param.put("userName",loginCase.getUserName());param.put("password",loginCase.getPassword());//设置请求头信息 设置headerpost.setHeader("content-type","application/json");//将参数信息添加到方法中StringEntity entity = new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//执行post方法HttpResponse response = TestConfig.defaultHttpClient.execute(post);//获取响应结果result = EntityUtils.toString(response.getEntity(),"utf-8");//储存cookies信息TestConfig.store = TestConfig.defaultHttpClient.getCookieStore();return result;}}

5、更新用户信息


import com.tester.config.TestConfig;
import com.tester.model.UpdateUserInfoCase;
import com.tester.model.User;
import com.tester.utils.DatabaseUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;public class UpdateUserInfoTest {@Test(dependsOnGroups = "loginTrue",description = "更改用户信息")public void updateUserInfo() throws IOException, InterruptedException {SqlSession session = DatabaseUtil.getSqlSession();UpdateUserInfoCase updateUserInfoCase = session.selectOne("updateUserInfoCase",1);System.out.println(updateUserInfoCase.toString());System.out.println(TestConfig.updateUserInfoUrl);/**//下边为写完接口的代码int result = getResult(updateUserInfoCase);//获取更新后的结果Thread.sleep(2000);User user = session.selectOne(updateUserInfoCase.getExpected(),updateUserInfoCase);System.out.println(user.toString());Assert.assertNotNull(user);Assert.assertNotNull(result);*/}@Test(dependsOnGroups = "loginTrue",description = "删除用户")public void deleteUser() throws IOException, InterruptedException {SqlSession session = DatabaseUtil.getSqlSession();UpdateUserInfoCase updateUserInfoCase = session.selectOne("updateUserInfoCase",2);System.out.println(updateUserInfoCase.toString());System.out.println(TestConfig.updateUserInfoUrl);/**//下边为写完接口的代码int result = getResult(updateUserInfoCase);Thread.sleep(2000);User user = session.selectOne(updateUserInfoCase.getExpected(),updateUserInfoCase);System.out.println(user.toString());Assert.assertNotNull(user);Assert.assertNotNull(result);*/}private int getResult(UpdateUserInfoCase updateUserInfoCase) throws IOException {HttpPost post = new HttpPost(TestConfig.updateUserInfoUrl);JSONObject param = new JSONObject();param.put("id",updateUserInfoCase.getUserId());param.put("userName",updateUserInfoCase.getUserName());param.put("sex",updateUserInfoCase.getSex());param.put("age",updateUserInfoCase.getAge());param.put("permission",updateUserInfoCase.getPermission());param.put("isDelete",updateUserInfoCase.getIsDelete());//设置请求头信息 设置headerpost.setHeader("content-type","application/json");//将参数信息添加到方法中StringEntity entity = new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//设置cookiesTestConfig.defaultHttpClient.setCookieStore(TestConfig.store);//声明一个对象来进行响应结果的存储String result;//执行post方法HttpResponse response = TestConfig.defaultHttpClient.execute(post);//获取响应结果result = EntityUtils.toString(response.getEntity(),"utf-8");System.out.println(result);return Integer.parseInt(result);}}

config类

1、报告配置


public class ExtentTestNGIReporterListener implements IReporter {//生成的路径以及文件名private static final String OUTPUT_FOLDER = "test-output/";private static final String FILE_NAME = "index.html";private ExtentReports extent;@Overridepublic void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {init();boolean createSuiteNode = false;if(suites.size()>1){createSuiteNode=true;}for (ISuite suite : suites) {Map<String, ISuiteResult> result = suite.getResults();//如果suite里面没有任何用例,直接跳过,不在报告里生成if(result.size()==0){continue;}//统计suite下的成功、失败、跳过的总用例数int suiteFailSize=0;int suitePassSize=0;int suiteSkipSize=0;ExtentTest suiteTest=null;//存在多个suite的情况下,在报告中将同一个一个suite的测试结果归为一类,创建一级节点。if(createSuiteNode){suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());}boolean createSuiteResultNode = false;if(result.size()>1){createSuiteResultNode=true;}for (ISuiteResult r : result.values()) {ExtentTest resultNode;ITestContext context = r.getTestContext();if(createSuiteResultNode){//没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。if( null == suiteTest){resultNode = extent.createTest(r.getTestContext().getName());}else{resultNode = suiteTest.createNode(r.getTestContext().getName());}}else{resultNode = suiteTest;}if(resultNode != null){resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());if(resultNode.getModel().hasCategory()){resultNode.assignCategory(r.getTestContext().getName());}else{resultNode.assignCategory(suite.getName(),r.getTestContext().getName());}resultNode.getModel().setStartTime(r.getTestContext().getStartDate());resultNode.getModel().setEndTime(r.getTestContext().getEndDate());//统计SuiteResult下的数据int passSize = r.getTestContext().getPassedTests().size();int failSize = r.getTestContext().getFailedTests().size();int skipSize = r.getTestContext().getSkippedTests().size();suitePassSize += passSize;suiteFailSize += failSize;suiteSkipSize += skipSize;if(failSize>0){resultNode.getModel().setStatus(Status.FAIL);}resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));}buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);}if(suiteTest!= null){suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));if(suiteFailSize>0){suiteTest.getModel().setStatus(Status.FAIL);}}}
//        for (String s : Reporter.getOutput()) {
//            extent.setTestRunnerOutput(s);
//        }extent.flush();}private void init() {//文件夹不存在的话进行创建File reportDir= new File(OUTPUT_FOLDER);if(!reportDir.exists()&& !reportDir .isDirectory()){reportDir.mkdir();}ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);// 设置静态文件的DNS//怎么样解决cdn.rawgit.com访问不了的情况htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);htmlReporter.config().setDocumentTitle("api自动化测试报告");htmlReporter.config().setReportName("api自动化测试报告");htmlReporter.config().setChartVisibilityOnOpen(true);htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);htmlReporter.config().setTheme(Theme.STANDARD);htmlReporter.config().setCSS(".node.level-1  ul{ display:none;} .node.level-1.active ul{display:block;}");extent = new ExtentReports();extent.attachReporter(htmlReporter);extent.setReportUsesManualConfiguration(true);}private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {//存在父节点时,获取父节点的标签String[] categories=new String[0];if(extenttest != null ){List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();categories = new String[categoryList.size()];for(int index=0;index<categoryList.size();index++){categories[index] = categoryList.get(index).getName();}}ExtentTest test;if (tests.size() > 0) {//调整用例排序,按时间排序Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {@Overridepublic int compare(ITestResult o1, ITestResult o2) {return o1.getStartMillis()<o2.getStartMillis()?-1:1;}});treeSet.addAll(tests.getAllResults());for (ITestResult result : treeSet) {Object[] parameters = result.getParameters();String name="";//如果有参数,则使用参数的toString组合代替报告中的namefor(Object param:parameters){name+=param.toString();}if(name.length()>0){if(name.length()>50){name= name.substring(0,49)+"...";}}else{name = result.getMethod().getMethodName();}if(extenttest==null){test = extent.createTest(name);}else{//作为子节点进行创建时,设置同父节点的标签一致,便于报告检索。test = extenttest.createNode(name).assignCategory(categories);}//test.getModel().setDescription(description.toString());//test = extent.createTest(result.getMethod().getMethodName());for (String group : result.getMethod().getGroups())test.assignCategory(group);List<String> outputList = Reporter.getOutput(result);for(String output:outputList){//将用例的log输出报告中test.debug(output);}if (result.getThrowable() != null) {test.log(status, result.getThrowable());}else {test.log(status, "Test " + status.toString().toLowerCase() + "ed");}test.getModel().setStartTime(getTime(result.getStartMillis()));test.getModel().setEndTime(getTime(result.getEndMillis()));}}}private Date getTime(long millis) {Calendar calendar = Calendar.getInstance();calendar.setTimeInMillis(millis);return calendar.getTime();}
}

2、用户路径配置

import lombok.Data;
import org.apache.http.client.CookieStore;
import org.apache.http.impl.client.DefaultHttpClient;/*** 将从工具里面获取的地址赋值到 这里面,,,**/
@Data
public class TestConfig {//登陆接口uripublic static String loginUrl;//更新用户信息接口uripublic static String updateUserInfoUrl;//获取用户列表接口uripublic static String getUserListUrl;//获取用户信息接口uripublic static String getUserInfoUrl;//添加用户信息接口public static String addUserUrl;//用来存储cookies信息的变量public static CookieStore store;//声明http客户端public static DefaultHttpClient defaultHttpClient;}

model类

import lombok.Data;@Data
public class AddUserCase {private int id;private String userName;private String password;private String sex;private String age;private String permission;private String isDelete;private String expected;
}
@Data
public class GetUserInfoCase {private  int id;private int userId;private String expected;
}
@Data
public class GetUserListCase {private String userName;private String age;private String sex;private String expected;
}
public enum InterfaceName {GETUSERLIST,LOGIN,UPDATEUSERINFO,GETUSERINFO,ADDUSERINFO
}
@Data
public class LoginCase {private int id;private String userName;private String password;private String expected;
}
@Data
public class UpdateUserInfoCase {private int id;private int userId;private String userName;private String sex;private String age;private String permission;private String isDelete;private String expected;
}

import lombok.Data;
@Data
public class User {private int id;private String userName;private String password;private String age;private String sex;private String permission;private String isDelete;@Overridepublic String toString(){return ("id:"+id+","+"userName:"+userName+","+"password:"+password+","+"age:"+age+","+"sex:"+sex+","+"permission:"+permission+","+"isDelete:"+isDelete+"}");}
}

utils类


import com.tester.model.InterfaceName;
import java.util.Locale;
import java.util.ResourceBundle;public class ConfigFile {private static ResourceBundle bundle= ResourceBundle.getBundle("application", Locale.CHINA);;public static String getUrl(InterfaceName name){//主地址String address = bundle.getString("test.url");String uri = "";//最终的测试地址String testUrl;if(name == InterfaceName.GETUSERLIST){uri = bundle.getString("getUserList.uri");}if(name == InterfaceName.LOGIN){uri = bundle.getString("login.uri");}if(name == InterfaceName.UPDATEUSERINFO){uri = bundle.getString("updateUserInfo.uri");}if(name == InterfaceName.GETUSERINFO){uri = bundle.getString("getUserInfo.uri");}if(name == InterfaceName.ADDUSERINFO){uri = bundle.getString("addUser.uri");}//最终拼接的地址testUrl = address + uri;return testUrl;}
}
public class DatabaseUtil {public static SqlSession getSqlSession() throws IOException {//获取配置的资源文件Reader reader = Resources.getResourceAsReader("databaseConfig.xml");//得到SqlSessionFactory,使用类加载器加载xml文件SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);//得到sqlsession对象,这个对象就能执行配置文件中的sql语句啦SqlSession session = factory.openSession();return session;}
}

模板–页面模板内容自己可以去百度找
image.png

package com.tester.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.testng.*;
import org.testng.xml.XmlSuite;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;public class TestReport implements IReporter {private long currentTime = System.currentTimeMillis();private SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年-MM月-dd日-HH时mm分ss秒");private Date date = new Date(currentTime);private String reportdate = formatter.format(date);// 定义生成测试报告的路径和文件名,为兼容Windows和Linux此处使用File.separator代替分隔符private String path = System.getProperty("user.dir")+File.separator+reportdate+".html";// 定义html样式模板所在路径private String templatePath = System.getProperty("user.dir")+File.separator+"template";private int testsPass = 0;private int testsFail = 0;private int testsSkip = 0;private String beginTime;private long totalTime;private String name = "PaaS平台自动化测试";/**public TestReport(){long currentTime = System.currentTimeMillis();SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年-MM月-dd日-HH时mm分ss秒");Date date = new Date(currentTime);name = formatter.format(date);}public TestReport(String name){this.name = name;if(this.name==null){long currentTime = System.currentTimeMillis();SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年-MM月-dd日-HH时mm分ss秒");Date date = new Date(currentTime);this.name = formatter.format(date);}}*/@Overridepublic void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {List<ITestResult> list = new ArrayList<ITestResult>();for (ISuite suite : suites) {Map<String, ISuiteResult> suiteResults = suite.getResults();for (ISuiteResult suiteResult : suiteResults.values()) {ITestContext testContext = suiteResult.getTestContext();IResultMap passedTests = testContext.getPassedTests();testsPass = testsPass + passedTests.size();IResultMap failedTests = testContext.getFailedTests();testsFail = testsFail + failedTests.size();IResultMap skippedTests = testContext.getSkippedTests();testsSkip = testsSkip + skippedTests.size();IResultMap failedConfig = testContext.getFailedConfigurations();list.addAll(this.listTestResult(passedTests));list.addAll(this.listTestResult(failedTests));list.addAll(this.listTestResult(skippedTests));list.addAll(this.listTestResult(failedConfig));}}this.sort(list);this.outputResult(list);}private ArrayList<ITestResult> listTestResult(IResultMap resultMap) {Set<ITestResult> results = resultMap.getAllResults();return new ArrayList<ITestResult>(results);}private void sort(List<ITestResult> list) {Collections.sort(list, new Comparator<ITestResult>() {@Overridepublic int compare(ITestResult r1, ITestResult r2) {if (r1.getStartMillis() > r2.getStartMillis()) {return 1;} else {return -1;}}});}private void outputResult(List<ITestResult> list) {try {List<ReportInfo> listInfo = new ArrayList<ReportInfo>();int index = 0;for (ITestResult result : list) {String tn = result.getTestContext().getCurrentXmlTest().getParameter("testCase");if(index==0){SimpleDateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");beginTime = formatter.format(new Date(result.getStartMillis()));index++;}long spendTime = result.getEndMillis() - result.getStartMillis();totalTime += spendTime;String status = this.getStatus(result.getStatus());List<String> log = Reporter.getOutput(result);for (int i = 0; i < log.size(); i++) {log.set(i, log.get(i).replaceAll("\"", "\\\\\""));}Throwable throwable = result.getThrowable();if(throwable!=null){log.add(throwable.toString().replaceAll("\"", "\\\\\""));StackTraceElement[] st = throwable.getStackTrace();for (StackTraceElement stackTraceElement : st) {log.add(("    " + stackTraceElement).replaceAll("\"", "\\\\\""));}}ReportInfo info = new ReportInfo();info.setName(tn);info.setSpendTime(spendTime+"ms");info.setStatus(status);info.setClassName(result.getInstanceName());info.setMethodName(result.getName());info.setDescription(result.getMethod().getDescription());info.setLog(log);listInfo.add(info);}Map<String, Object> result = new HashMap<String, Object>();result.put("testName", name);result.put("testPass", testsPass);result.put("testFail", testsFail);result.put("testSkip", testsSkip);result.put("testAll", testsPass+testsFail+testsSkip);result.put("beginTime", beginTime);result.put("totalTime", totalTime+"ms");result.put("testResult", listInfo);Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();String template = this.read(templatePath);BufferedWriter output = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(new File(path)),"UTF-8"));template = template.replaceFirst("\\$\\{resultData\\}", Matcher.quoteReplacement(gson.toJson(result)));output.write(template);output.flush();output.close();} catch (IOException e) {e.printStackTrace();}}private String getStatus(int status) {String statusString = null;switch (status) {case 1:statusString = "成功";break;case 2:statusString = "失败";break;case 3:statusString = "跳过";break;default:break;}return statusString;}public static class ReportInfo {private String name;private String className;private String methodName;private String description;private String spendTime;private String status;private List<String> log;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public String getSpendTime() {return spendTime;}public void setSpendTime(String spendTime) {this.spendTime = spendTime;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public List<String> getLog() {return log;}public void setLog(List<String> log) {this.log = log;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}}private String read(String path) {File file = new File(path);InputStream is = null;StringBuffer sb = new StringBuffer();try {is = new FileInputStream(file);int index = 0;byte[] b = new byte[1024];while ((index = is.read(b)) != -1) {sb.append(new String(b, 0, index));}return sb.toString();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();}}return null;}
}

配置配置类

SQLMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 命名空间mapper,如果有多个mapper文件,这个必须唯一 -->
<mapper namespace="com.tester.model"><!--获取登陆接口case--><select id="loginCase" parameterType="Integer"  resultType="com.tester.model.LoginCase">select * from loginCasewhere id = #{id};</select><!--添加用户接口case--><select id="addUserCase" parameterType="Integer" resultType="com.tester.model.AddUserCase">select * from addUserCase where id=#{id};</select><!--获取用户信息case--><select id="getUserInfoCase" parameterType="Integer" resultType="com.tester.model.GetUserInfoCase"><!-- SQL语句 -->select * from getUserInfoCase where id=#{id};</select><!--获取用户列表case--><select id="getUserListCase" parameterType="Integer" resultType="com.tester.model.GetUserListCase"><!-- SQL语句 -->select * from getUserListCase where id=#{id};</select><!--更新/删除用户信息case--><select id="updateUserInfoCase" parameterType="Integer" resultType="com.tester.model.UpdateUserInfoCase">select * from updateUserInfoCase where id = #{id};</select><!--添加用户接口--><select id="addUser" parameterType="com.tester.model.AddUserCase" resultType="com.tester.model.User">select * from user whereuserName=#{userName}and password=#{password}and sex=#{sex}and age=#{age}and permission=#{permission}and isDelete=#{isDelete};</select><!--获取用户信息--><select id="getUserInfo" parameterType="com.tester.model.GetUserInfoCase" resultType="com.tester.model.User"><!-- SQL语句 -->select * from user whereid=#{userId};</select><!--获取用户列表--><select id="getUserList" parameterType="com.tester.model.GetUserListCase" resultType="com.tester.model.User"><!-- SQL语句 -->select * from user<trim prefix="WHERE" prefixOverrides="and"><if test="null != userName and '' !=userName">AND userName=#{userName}</if><if test="null != sex and '' !=sex">AND sex=#{sex}</if><if test="null != age and '' !=age">AND age=#{age}</if></trim>;</select><!--获取更新后的数据--><select id="getUpdateUserInfo" parameterType="com.tester.model.UpdateUserInfoCase" resultType="com.tester.model.User">select * from user<trim prefix="WHERE" prefixOverrides="and"><if test="null != userName and '' !=userName">AND userName=#{userName}</if><if test="null != sex and '' !=sex">AND sex=#{sex}</if><if test="null != age and '' !=age">AND age=#{age}</if><if test="null != permission and '' !=permission">AND permission=#{permission}</if><if test="null != isDelete and '' !=isDelete">AND isDelete=#{isDelete}</if></trim>And id = #{userId};</select>
</mapper>

spring boot全局配置

test.url=http://localhost:8888#登陆接口uri
login.uri=/login#更新用户信息接口uri
updateUserInfo.uri=/updateUserInfo#获取用户列表接口uri
getUserList.uri=/getUserListInfo#获取用户信息接口uri
getUserInfo.uri=/getUserInfo#添加用户接口uri
addUser.uri=/addUser

databaseConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 注册对象的空间命名 --><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!-- 1.加载数据库驱动 --><property name="driver" value="com.mysql.jdbc.Driver"/><!-- 2.数据库连接地址 --><property name="url" value="jdbc:mysql://127.0.0.1:3306/course"/><!-- 数据库用户... --><property name="username" value="root"/><!-- 数据库密码... --><property name="password" value="123456"/></dataSource></environment></environments><!-- 注册映射文件:java对象与数据库之间的xml文件路径! -->
<mappers><mapper resource="mapper/SQLMapper.xml"/>
</mappers>
</configuration>

testng.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="用户管理系统测试套件"><test name="用户管理系统测试用例"><classes><class name="com.tester.cases.LoginTest"><methods><include name="loginTrue"/><include name="loginFalse"/></methods></class><class name="com.tester.cases.AddUserTest"><methods><include name="addUser"/></methods></class><class name="com.tester.cases.GetUserInfoTest"><methods><include name="getUserInfo"/></methods></class><class name="com.tester.cases.UpdateUserInfoTest"><methods><include name="updateUserInfo"/><include name="deleteUser"/></methods></class><class name="com.tester.cases.GetUserInfoListTest"><methods><include name="getUserListInfo"/></methods></class></classes></test><listeners><listener class-name="com.tester.config.ExtentTestNGIReporterListener" /></listeners></suite>

测试

1、前提条件

第一步:首先mock数据

[{"description":"登陆接口,成功后返回cookies","request":{"uri":"/login","method":"post","json":{"userName":"abc","password":"123"}},"response":{"cookies":{"login":"true"},"text":"true"}},{"description":"获取用户信息","request":{"uri":"/getUserInfo","method":"post","json":{"userId":"1"},"cookies":{"login":"true"}},"response":{"json":{"id":"1","userName":"zhangsan","password":"123456","age":"20","sex":"0","permission":"0","isDelete":"0"}}},{"description":"获取用户信息接口","request":{"uri":"/getUserListInfo","method":"post","json":{"sex":"0"},"cookies":{"login":"true"}},"response":{"json":[{"id":"1","userName":"zhangsan","password":"123456","age":"20","sex":"0","permission":"0","isDelete":"0"},{"id":"3","userName":"wangwu","password":"123456","age":"30","sex":"0","permission":"1","isDelete":"0"},{"id":"5","userName":"zhang1","password":"123","age":"20","sex":"0","permission":"0","isDelete":"0"}]}},{"description":"增加用户接口","request":{"uri":"/addUser","method":"post","json":{"userName":"zhao9","password":"zhaozhao","sex":"0","age":"35","permission":"1","isDelete":"0"},"cookies":{"login":"true"}},"response":{"text":"true"}},{"description":"增加用户接口","request":{"uri":"/updateUserInfo","method":"post","json":{"userId":"2","userName":"hahahaha"},"cookies":{"login":"true"}},"response":{"text":"true"}},{"description":"删除用户接口","request":{"uri":"/deleteUser","method":"post","json":{"userId":"8"},"cookies":{"login":"true"}},"response":{"text":"true"}}
]

第二步:启动服务

java -jar ./moco-runner-0.11.0-standalone.jar http -p 8888 -c userManager.json
启动服务注意事项

  • 一定的在moco-runner-0.11.0-standalone.jar包下启动,否则找不到包

第三步::使用postman测试mock服务是否可用

image.png

第四步:数据的准备 准备用户名等于 abc的数(用于登录结果的验证)

image.png

2、已登录成功案例测试为例:

自己在测试中值得的问题

  • java -jar ./moco-runner-0.11.0-standalone.jar http -p 8888 -c userManager.json 启动的端口要和application.properties文件中配置的一样
  • 在getResult()方法中,塞值一定要和mock中请求参数的字段一样
  • 请求的方式要设置好,是已JSON还是key-value的方式
  • 在查询数据库的时候,model定义的字段民要和数据库一致

2.1、直接在用例里面测试验证是否能用

image.png

2.2、在xml测试套件是否可用

image.png
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/164188.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

了解 Elasticsearch 自动生成的文档 _id:重复是一个问题吗?

Elasticsearch 中自动生成的文档 ID 当你在未指定 ID 的情况下对文档建立索引时&#xff0c;Elasticsearch 会自动为该文档生成唯一的 ID。 该 ID 是 Base64 编码的 UUID&#xff0c;由多个部分组成&#xff0c;每个部分都有特定的用途。 ID 生成过程针对索引速度和存储效率进…

2023年中国人力资源咨询发展历程及市场规模前景分析[图]

人力资源咨询是企业借助外部智力资源提高自身管理水平和效率的重要路径&#xff0c;属于管理咨询业的一个重要分支, 一方面&#xff0c;人力资源咨询要为企业提供基础的人力资源外包服务&#xff1b;另一方面&#xff0c;人力资源咨询要为企业提供专业化、职业化现代人力资源管…

用CSS+SVG做一个优雅的环形进度条

开门见山 先上最终效果图&#xff1a;&#xff08;Demo 传送门&#xff09; 其中进度、尺寸、环宽和颜色都可以非常方便地进行控制。 核心原理&#xff1a; 利用两个重叠的圆环形&#xff0c;通过对上层圆环弧长的控制来表示进度&#xff0c;下层圆环则作为辅助&#xff0c;…

开源云财务软件,财务软件源码,永久免费财务软件

纷析云SAAS云财务软件开源版 包含账套、凭证字、科目、期初、币别、账簿、报表、凭证、结账 技术交流群 扫码添加客服进群 商业版地址 纷析云商业版https://f3.fenxi365.com/ 正式环境&#xff0c;可注册账号直接使用或测试 功能对比 功能模块开源版商业版[技术重构]凭证✔…

蜻蜓c影视追剧系统-多个小程序添加说明

多小程序添加设置 蜻蜓c影视追剧 支持多小程序添加&#xff0c;也就是可以管理不同前端的小程序。 此处id 对应前端小程序的mp值 关于添加小程序&#xff1a; 此处有所有填写内容的参考方式&#xff0c;要注意是必须开通了微信支付才可以添加&#xff0c;这里需要添加证书信息…

高精度时间测量(TDC)电路MS1022

MS1022 是一款高精度时间测量电路&#xff0c;内部集成了模拟比 较器、模拟开关、施密特触发器等器件&#xff0c;从而大大简化了外 围电路。同时内部增加了第一波检测功能&#xff0c;使抗干扰能力大 大提高。通过读取第一个回波脉冲的相对宽度&#xff0c;用户可以获 得接…

微信小程序文本横向无缝滚动

背景&#xff1a; 微信小程序中列表宽度不够长&#xff0c;其中某字段显示不完整&#xff0c;因此要使其自动滚动。 &#xff08;最初看网上很多用定时器实现&#xff0c;但他们的案例中都只是一个横幅、用定时器也无所谓。但是我的需求中是一个上下无限滚动的列表&#xff0c;…

离线语音与IoT结合:智能家居发展新增长点

离线语音控制和物联网&#xff08;IoT&#xff09;相结合在家居中具有广泛的应用和许多优势。离线语音控制是指在设备在本地进行语音识别和处理&#xff0c;而不需要依赖云服务器进行处理。IoT是指借助网络&#xff0c;通过手机APP、小程序远程控制家居设备。 启英泰伦基于AI语…

【面试系列】Vue

引言&#xff1a;下面是一些常见的 Vue 面试题和对应的答案 目录 1. Vue 是什么&#xff1f;它有哪些特点&#xff1f;2. Vue 的生命周期有哪些&#xff1f;请简要描述每个生命周期的作用。3. Vue 组件间通信的有哪些方式&#xff1f;4. Vue 的 computed 和 watch 的区别是什么…

python安装gdal

下载whl https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal 安装 pip install GDAL-3.1.4-cp36-cp36m-win_amd64.whl

iMazing苹果用户手机备份工具 兼容最新的iOS16操作系统

现在距离苹果秋季新品发布会已过去月余&#xff0c;新iPhone 14系列和新版的iOS 16操作系统也如约与我们见面了&#xff0c;相信大家在9月初抢购的iPhone 14也基本到手了&#xff0c;但随之到来的数据资料备份迁移却是一件令人头大的事情&#xff0c;使用官方提供的iTunes软件卡…

easyrecovery2024数据恢复软件最新版本下载

easyrecovery是PC上数据恢复领域相当给力的应用软件之一&#xff0c;它具有操作安全&#xff0c;价格便宜&#xff0c;支持用户自主操作等特点&#xff0c;能支持从各种存储介质恢复删除、格式化或者丢失的文件&#xff0c;从任何存储介质设备上的损坏&#xff0c;删除&#xf…

搭建react项目

一、环境准备 1、安装node 官网下载安装&#xff1a;https://nodejs.org/en 注&#xff1a; npm5.2以后&#xff0c;安装node会自动安装npm和npx 2、安装webpack npm install -g webpack3、安装create-react-app npm install -g create-react-app二、创建react项目 1、初…

DFS(分布式文件系统)与 DFSR(分布式文件系统复制)的区别

DFS&#xff08;分布式文件系统&#xff09;和 DFSR&#xff08;分布式文件系统复制&#xff09;是两种不同的技术&#xff0c;尽管它们在名称上有一些相似之处&#xff0c;但它们的用途和功能有所不同。 DFS&#xff08;分布式文件系统&#xff09; DFS 是一种用于创建和管理…

GEE:为机器学习算法(随机森林、支持矢量机等)加入膨胀/腐蚀特征

作者:CSDN @ _养乐多_ 腐蚀和膨胀是数学形态学图像处理中的两个基本操作,用于修改和分析二值图像(包含只有两个像素值的图像,通常是黑和白)。 腐蚀和膨胀操作可以作为机器学习中的特征变量,用来分类,比如在博客《GEE:随机森林分类教程(样本制作、特征添加、训练、精…

Flink学习---15、FlinkCDC(CDC介绍、案例实操)

星光下的赶路人star的个人主页 未来总是藏在迷雾中让人胆怯&#xff0c;但当你踏入其中&#xff0c;便会云开雾散 文章目录 1、CDC简介1.1 什么是CDC1.2 CDC的种类1.3 Flink-CDC 2、FlinkCDC案例实操2.1 开启MySQL Binlog并重启MySQL2.2 FlinkSQL方式的应用2.2.1 导入依赖2.2.2…

解密zkLogin:探索前沿的Sui身份验证解决方案

由于钱包复杂性导致的新用户入门障碍是区块链中一个长期存在的问题&#xff0c;而zkLogin是其简单的解决方案。通过使用前沿的密码学和技术&#xff0c;zkLogin既优雅又复杂。本文深入探讨了zkLogin的工作原理&#xff0c;涵盖了用户和开发者的安全性方面&#xff0c;并解释了S…

如何打造独立站?这4个要点必须做到!

“什么是独立站”独立站指的是个人或小团队独立创建和管理的网站&#xff0c;与依赖于第三方平台的博客、社交媒体或在线商店不同。独立站的所有权和控制权完全归个人或小团队所有&#xff0c;因此具有更大的自主性和独立性&#xff0c;不受第三方平台的限制。 独立站是由个人…

抽象数据库

在刚刚的文章中&#xff0c;完成了无范式到三级范式的过程 : 遵循原子性。即&#xff0c;表中字段的数据&#xff0c;不可以再拆分。 在满足第一范式的情况下&#xff0c;遵循唯一性&#xff0c;消除部分依赖。即&#xff0c;表中任意一个主键或任意一组联合主键&#xff0c…

企业数字化转型时,会遇到的5大挑战

企业数字化转型时&#xff0c;会遇到的5大挑战添加链接描述 数字化转型已然是当今商业战略的一大基石&#xff0c;根据Gartner的《2023年度董事会调查》显示&#xff0c;有89%的企业将数字业务视为其增长的核心。但该研究的另一项统计数据也显示&#xff1a;在这些企业中&…