当前位置: 首页 > news >正文

【七:(测试用例)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

相关文章:

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

目录 1、目录结构的相关类cases类1、添加用户 AddUserTest2、获取用户列表信息 GetUserInfoListTest3、获取用户信息 GetUserInfoTest4、登录测试5、更新用户信息 config类1、报告配置2、用户路径配置 model类utils类 配置配置类SQLMapper.xmlspring boot全局配置databaseConfi…...

哪些数据应该先治理

在我们提供的数据中&#xff0c;哪些是真正需要优先治理的呢&#xff1f;这是任何数据治理项目开始之前都需要解决的问题。正确地确定了数据治理的优先级&#xff0c;不仅可以帮助我们将有限的资源用在刀刃上&#xff0c;更能实现数据治理的最大价值。下面数聚就深度为企业管理…...

No module ‘xformers‘. Proceeding without it.

一、背景&#xff1a; 运行提示 No module xformers. Proceeding without it. 二、分析 1、xformers是SD的加速模块&#xff0c;没有他可以继续运行&#xff0c;可以正常生成图片。只是xformers可以帮助提升图片的生成速度。 2、安装完SD后&#xff0c;启动出现xformers未安…...

Stable Diffusion WebUI报错RuntimeError: Torch is not able to use GPU解决办法

新手在安装玩Stable Diffusion WebUI之后会遇到各种问题&#xff0c; 接下来会慢慢和你讲解如何解决这些问题。 在我们打开Stable Diffusion WebUI时会报错如下&#xff1a; RuntimeError: Torch is not able to use GPU&#xff1b;add --skip-torch-cuda-test to COMMANDL…...

金融信息化研究所与YashanDB等单位启动金融多主数据库应用行动计划

10月13日&#xff0c;2023金融业 数据库技术大会在京成功召开。会上&#xff0c;金融信息化研究所与崖山数据库YashanDB、阿里巴巴、奥星贝斯、达梦、南大通用、华为、天翼云、万里数据库、优炫数据库共同启动金融多主数据库应用行动计划&#xff0c;并成立金融多主数据库应用…...

工具篇之Axure RP 10的使用

引言 最近在学习原型图&#xff0c;针对画原型图的工具&#xff0c;反复对比墨刀、Axure、xiaopiu后&#xff0c;最终选择Axure。 接下来&#xff0c;我便从Axure RP 10的下载、安装、中文字体、授权等几个方面&#xff0c;来介绍Axure。 一、背景 Axure是一款强大的原型设计…...

C#选择排序(Selection Sort)算法

选择排序&#xff08;Selection Sort&#xff09;原理介绍 选择排序&#xff08;Selection Sort&#xff09;是一种简单的排序算法&#xff0c;其实现原理如下&#xff1a; 遍历待排序数组&#xff0c;从第一个元素开始。 假设当前遍历的元素为最小值&#xff0c;将其索引保存…...

【Mysql】InnoDB数据页结构(五)

概述 页是InnoDB存储引擎管理存储空间的基本单位&#xff0c;一个页的大小默认是16KB 。InnoDB 为了不同的目的而设计了许多种不同类型的页 &#xff0c;比如存放记录的索引页&#xff0c;存放表空间头部信息的页&#xff0c;存放 Insert Buffer信息的页&#xff0c;存放 INOD…...

Golang中的type关键字

type关键字在Go语言中有五种用法: 定义结构体定义接口类型别名类型定义类型开关其中,定义结构体和定义接口是Go语言中常用的类型定义方式,类型别名和类型定义则是为了方便程序员使用而设计的,而类型开关则是Go语言中比较特殊的一种类型定义方式。 定义结构体 结构体是由一…...

网站管家机器人在为企业获客方面起什么作用?

随着科技的不断进步和人们对便捷服务的需求增加&#xff0c;网站管家机器人成为了现代企业获客的重要工具。作为一种基于人工智能技术的在线助手&#xff0c;网站管家机器人可以与访问企业网站的用户进行智能对话&#xff0c;并提供即时的帮助和解答。 网站管家机器人在为企业获…...

竞赛选题 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…...

零基础学习HTML5

1. 使用软件 vscode 谷歌浏览器 vscode下载地址&#xff1a;https://code.visualstudio.com/ 谷歌可以使用360软件管家安装 2. 安装插件 在vscode中安装插件&#xff1a;open in browser&#xff0c;点击Extensions后搜索对应插件名然后点击安装Install 安装完成后可在htm…...

Jenkins 部署 Maven项目很慢怎么办?

Jenkins 部署 Maven项目很慢怎么办&#xff1f; 答案是&#xff1a;使用阿里云的Maven仓库 <mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/pub…...

关于刷题时使用数组的小注意事项

&#x1f4af; 博客内容&#xff1a;关于刷题时使用数组的小技巧 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&#xff01; &#x1f496; 欢迎大家&#…...

【MySQL】面试题

引言 &#xff1a;MySQL面试题及答案 【最新版】 目录 1、NOW&#xff08;&#xff09;和CURRENT_DATE&#xff08;&#xff09;有什么区别&#xff1f;2、CHAR和VARCHAR的区别&#xff1f;3、主键索引与唯一索引的区别4、MySQL中有哪些不同的表格&#xff1f;5、SQL的生命周期…...

Pytorch训练深度强化学习时CPU内存占用一直在快速增加

最近在用MATD3算法解决多机器人任务&#xff0c;但是在训练过程中&#xff0c;CPU内存一直在增加&#xff08;注意&#xff0c;不是GPU显存&#xff09;。我很头疼&#xff0c;以为是算法代码出了问题&#xff0c;导致了内存泄漏&#xff0c;折腾了1天也没解决。后来用memory_p…...

git第一次推送出现推送被拒绝

前言 git 第一次推送出现以下错误 ! [rejected] master -> master (fetch first) error: failed to push some refs to ‘https://gitee.com/fengshangyunwang/iot-front-end.git’ hint: Updates were rejected because the remote contains work that you do hint: not …...

CRC16计算FC(博途SCL语言)

CRC8的计算FC,相关链接请查看下面文章链接: 博途SCL CRC8 计算FC(计算法)_博途怎么计算crc_RXXW_Dor的博客-CSDN博客关于CRC8的计算网上有很多资料和C代码,这里不在叙述,这里主要记录西门子的博途SCL完成CRC8的计算过程, CRC校验算法,说白了,就是把需要校验的数据与多项式…...

setsockopt()函数的用法

setsockopt() 函数是一个用于设置套接字选项的函数&#xff0c;通常在网络编程中使用。它用于配置套接字的各种参数和选项&#xff0c;以满足特定的需求。setsockopt() 函数的作用是设置指定套接字选项的值。 setsockopt() 函数的一般用法&#xff1a; int setsockopt(int soc…...

【AOP系列】6.缓存处理

在Java中&#xff0c;我们可以使用Spring AOP&#xff08;面向切面编程&#xff09;和自定义注解来做缓存处理。以下是一个简单的示例&#xff1a; 首先&#xff0c;我们创建一个自定义注解&#xff0c;用于标记需要进行缓存处理的方法&#xff1a; import java.lang.annotat…...

云函数cron-parser解析时区问题

1、问题 云函数部署后cron-parser解析0点会变成8点 考虑可能是时区的问题 然后看文档发现果然有问题&#xff0c;云函数环境是utc0 2、解决 看了半天cron-parser文档发现 Using Date as an input can be problematic specially when using the tz option. The issue bein…...

Android11修改自动允许连接到建议的WLAN网络

客户的app需要连接指定的wifi,但是会提示下面的对话框(是否允许系统连接到建议的WLAN网络?): 客户需求:不提示这个对话框自动允许。 根据字符串定位到frameworks\opt\net\wifi\service\java\com\android\server\wifi\WifiNetworkSuggestionsManager.java 中的privat…...

基于Qt HTTP应用程序项目案例

文章目录 主项目入口项目子头文件httpwindow.hhttpwindow.h源文件httpwindow.cppui文件效果演示主项目入口 main函数创建对象空间,确认窗口的大小和坐标。 #include <QApplication> #include <QDir> #include...

OpenGL —— 2.7、绘制多个自旋转的贴图正方体(附源码,glfw+glad)

源码效果 C源码 纹理图片 需下载stb_image.h这个解码图片的库&#xff0c;该库只有一个头文件。 具体代码&#xff1a; vertexShader.glsl #version 330 corelayout(location 0) in vec3 aPos; layout(location 1) in vec2 aUV;out vec2 outUV;uniform mat4 _modelMatrix; …...

linux之perf(8)annotate标注

Linux之perf(8)annotate标注 Author&#xff1a;Onceday Date&#xff1a;2023年10月12日 漫漫长路&#xff0c;才刚刚开始… 注&#xff1a;该文档内容采用了GPT4.0生成的回答&#xff0c;部分文本准确率可能存在问题。 参考文档: Tutorial - Perf Wiki (kernel.org)perf…...

【广州华锐互动】VR建筑安全培训体验为建筑行业人才培养提供有力支持

随着建筑行业的快速发展&#xff0c;建筑施工安全问题日益受到广泛关注。然而&#xff0c;传统的安全培训方式往往缺乏实践性和真实性&#xff0c;难以让员工真正掌握安全操作技能。近年来&#xff0c;虚拟现实(VR)技术的广泛应用为建筑施工安全培训提供了新的机遇。 虚拟现实技…...

【Javascript保姆级教程】运算符

文章目录 前言一、运算符是什么二、赋值运算符2.1 如何使用赋值运算符2.2 示例代码12.3 示例代码2 三、自增运算符3.1 运算符3.2 示例代码13.3 示例代码2 四、比较运算符4.1 常见的运算符4.2 如何使用4.3 示例代码14.4 示例代码2 五、逻辑运算符逻辑运算符列举 六、运算符优先级…...

图论与网络优化

2.概念与计算 2.1 图的定义 2.1.1 定义 图(graph) G G G 是一个有序的三元组&#xff0c;记作 G < V ( G ) , E ( G ) , ψ ( G ) > G<V(G),E(G),\psi (G)> G<V(G),E(G),ψ(G)>。 V ( G ) V(G) V(G) 是顶点集。 E ( G ) E(G) E(G) 是边集。 ψ ( G ) \…...

【论文复现】基于多模态深度学习方法的单细胞多组学数据聚类(【生物信息学】实验二:多组学数据融合:scMDC)

目录 一、实验介绍 1. 论文&#xff1a;基于多模态深度学习方法的单细胞多组学数据聚类 Abstract 2. Github链接 二、实验环境 0. 作者要求 1. 环境复现 实验一 实验二&#xff08;本实验&#xff09; 2. 库版本介绍 实验一 实验二 3. IDE 三、实验内容 1. 用法…...

mysql按指定字符截取

1、使用SUBSTRING函数进行截取 语法&#xff1a;str是要截取的字符串&#xff0c;pos是起始位置&#xff0c;len是要截取的长度 SUBSTRING(str, pos, len)例子 SELECT SUBSTRING(Hello, World!, 1, 5);返回"Hello"。其中&#xff0c;起始位置为1&#xff0c;截取的…...