精华内容
下载资源
问答
  • 接口测试框架 https://blog.csdn.net/qq_15283475/article/details/106494544 com.bjci.api #包名 ApiInfoModel.java ## Api类 ExcelUtills.java ##操作excel的类 HandlsUtils.java ##根据judge分发不同的...
  • Java接口自动化测试框架

    千次阅读 2019-11-21 12:07:20
    自动化测试框架 1. 测试框架TestNG 1.1 适合测试人员使用的原因 (1)比Junit涵盖功能更全面的测试框架 (2)Junit更适合隔离性比较强的单元测试 (3)TestNG更适合复杂的集成测试 1.2 基本介绍 (1)基本注解:...

    一. 自动化测试框架

    在这里插入图片描述

    1. 测试框架TestNG

    1.1 适合测试人员使用的原因

    (1)比Junit涵盖功能更全面的测试框架
    (2)Junit更适合隔离性比较强的单元测试
    (3)TestNG更适合复杂的集成测试

    1.2 基本介绍

    (1)基本注解:决定执行顺序
    例:
    @Test:标记一个类或方法作为测试的一部分
    @beforeTest、@afterTest:做前置或后置处理
    (2)属性
    例:
    groups:分组测试
    dependsOnGroups:依赖测试
    description:描述
    (3)测试套件
    组织测试类一起执行的或者一组行为的测试用例的集合,由一个XML文件标记

    2. 测试报告ExtentReport

    2.1 添加测试类

    ExtentTestNGIReporterListener

    2.2 基本配置

    在测试套件中 @listener标签下添加监听器

    3. HttpClient

    一个HTTP客户端编程工具,可用来发送请求、接收响应

    4. MyBatis

    持久层框架,支持定制化 SQL、存储过程以及高级映射。
    可以使用简单的 XML 或注解来配置和映射原生信息。

    5. MySQL

    存储测试用例

    二. 编写步骤及文件目录结构

    1. 测试用例的表结构设计

    在这里插入图片描述

    2. 基础配置文件设计

    在这里插入图片描述

    2.1 pom.xml:引入第三方依赖包

    配置httpclient、json、mybatis、mysql、lombok、extentreports、testng的各种依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>AutoTest</artifactId>
            <groupId>Chapter</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>Chapter12</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.1.2</version>
            </dependency>
            <dependency>
                <groupId>org.json</groupId>
                <artifactId>json</artifactId>
                <version>20170516</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.4</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.14</version>
            </dependency>
            <dependency>
                <groupId>com.relevantcodes</groupId>
                <artifactId>extentreports</artifactId>
                <version>2.41.1</version>
            </dependency>
            <dependency>
                <groupId>com.vimalselvam</groupId>
                <artifactId>testng-extentsreport</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>com.aventstack</groupId>
                <artifactId>extentreports</artifactId>
                <version>3.0.6</version>
            </dependency>
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>6.10</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.0.4</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
        </dependencies>
    </project>
    

    2.2 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.cj.jdbc.Driver"/>
                    <!-- 2.数据库连接地址 -->
                    <property name="url" value="jdbc:mysql://localhost:3306/course?serverTimezone=GMT"/>
                    <!-- 数据库用户... -->
                    <property name="username" value="root"/>
                    <!-- 数据库密码... -->
                    <property name="password" value="12345678"/>
                </dataSource>
            </environment>
        </environments>
        <!-- 注册映射文件:java对象与数据库之间的xml文件路径! -->
        <mappers>
            <mapper resource="mapper/SQLMapper.xml"/>
        </mappers>
    </configuration>
    

    2.3 application.properties:接口信息配置文件

    test.url=http://localhost:8080
    
    #登陆接口uri
    login.uri=/v1/login
    

    2.4 testng.xml:用以执行所有testng的测试套件

    <?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>       
            </classes>
        </test>
        <listeners>
            <listener class-name="com.tester.config.ExtentTestNGIReporterListener"/>
        </listeners>
    </suite>
    

    2.5 SQLMapper.xml:用以存储所有测试用例的SQL语句

    <?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 namespace="com.tester.model">
        <!--获取登陆接口case-->
        <select id="loginCase" parameterType="Integer" resultType="com.tester.model.LoginCase">
            select *from logincase where id=#{id};
        </select>
     </mapper>
    

    3. model层、config层、utils层、cases层

    在这里插入图片描述

    3.1 model层:放置各个接口的数据配置文件+InterfaceName枚举

    3.1.1 放置登录接口的数据配置文件LoginCase.java

    package com.tester.model;
    
    import lombok.Data;
    
    @Data
    public class LoginCase {
        private int id;
        private String userName;
        private String password;
        private String expected;
    }
    

    3.1.2 InterfaceName.java

    package com.tester.model;
    
    public enum InterfaceName {
        LOGIN
    }
    

    3.2 Config层:配置信息TestConfig类+ExtentTestNGReportListener类

    3.2.1TestConfig类:声明各个测试用例的URL、和之后要用的一些全局变量

    package com.tester.config;
    
    import lombok.Data;
    import org.apache.http.client.CookieStore;
    import org.apache.http.impl.client.DefaultHttpClient;
    
    
    
    @Data
    public class TestConfig {
        //登陆接口uri
        public static String loginUrl;
        //声明http客户端
        public static DefaultHttpClient defaultHttpClient;
        //用来存储cookies信息的变量
        public static CookieStore store;
    }
    

    3.2.2 ExtentTestNGReportListener类:测试报告配置

    package com.tester.config;
    
    
    import com.aventstack.extentreports.ExtentReports;
    import com.aventstack.extentreports.ExtentTest;
    import com.aventstack.extentreports.ResourceCDN;
    import com.aventstack.extentreports.Status;
    import com.aventstack.extentreports.model.TestAttribute;
    import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
    import com.aventstack.extentreports.reporter.configuration.ChartLocation;
    import com.aventstack.extentreports.reporter.configuration.Theme;
    import org.testng.*;
    import org.testng.xml.XmlSuite;
    
    import java.io.File;
    import java.util.*;
    
    public class ExtentTestNGIReporterListener implements IReporter {
        //生成的路径以及文件名
        private static final String OUTPUT_FOLDER = "test-output/";
        private static final String FILE_NAME = "index.html";
    
        private ExtentReports extent;
    
        @Override
        public 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>() {
                    @Override
                    public 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组合代替报告中的name
                    for(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();
        }
    }
    

    3.3 utils层: 抽取公用的方法ConfigFile类+DatabaseUtil类

    3.3.1 ConfigFile类:对各个测试用例的URL进行赋值

    package com.tester.utils;
    
    import com.tester.model.InterfaceName;
    
    import java.util.Locale;
    import java.util.ResourceBundle;
    
    public class ConfigFile {
        public 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.LOGIN){
                uri=bundle.getString("login.uri");
            }
            
            testUrl=address+uri;
            return testUrl;
        }
    }
    

    3.3.2 DatabaseUtil类:配置一个getSqlSession()方法

    作用是执行配置文件SQLMapper中的SQL语句

    package com.tester.utils;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.Reader;
    
    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;
        }
    }
    

    3.4 cases层:用来放接口的测试用例

    package com.tester.cases;
    
    import com.tester.config.TestConfig;
    import com.tester.model.InterfaceName;
    import com.tester.model.LoginCase;
    import com.tester.utils.ConfigFile;
    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.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(){
            TestConfig.getUserInfoUrl= ConfigFile.getUrl(InterfaceName.GETUSERINFO);
            TestConfig.getUserListUrl=ConfigFile.getUrl(InterfaceName.GETUSERLIST);
            TestConfig.addUserUrl=ConfigFile.getUrl(InterfaceName.ADDUSERINFO);
            TestConfig.loginUrl=ConfigFile.getUrl(InterfaceName.LOGIN);
            TestConfig.updateUserInfoUrl=ConfigFile.getUrl(InterfaceName.UPDATEUSERINFO);
    
            TestConfig.defaultHttpClient=new DefaultHttpClient();
        }
    
        @Test(groups = "loginTrue",description = "用户成功登陆接口")
        public void loginTrue() throws IOException {
            SqlSession session= DatabaseUtil.getSqlSession();
            LoginCase loginCase=session.selectOne("loginCase",1);
            System.out.println(loginCase.toString());
            System.out.println(TestConfig.loginUrl);
    
            //下边的代码为写完接口的测试代码
            String result=getResult(loginCase);
            //处理结果,就是判断返回结果是否符合预期
            Assert.assertEquals(loginCase.getExpected(),result);
        }
    
    
    
        @Test(groups = "loginFalse",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 {
            //下边的代码为写完接口的测试代码
            HttpPost post=new HttpPost(TestConfig.loginUrl);
            JSONObject param=new JSONObject();
            param.put("userName",loginCase.getUserName());
            param.put("password",loginCase.getPassword());
            //设置请求头信息,设置header
            post.setHeader("content-type","application/json");
            //将参数信息添加到方法中
            StringEntity entity=new StringEntity(param.toString(),"utf-8");
            post.setEntity(entity);
            //声明一个对象来进行响应结果的存储
            String result;
            //执行post方法
            HttpResponse response=TestConfig.defaultHttpClient.execute(post);
            //获取响应结果
            result= EntityUtils.toString(response.getEntity(),"utf-8");
            System.out.println(result);
    
            TestConfig.store=TestConfig.defaultHttpClient.getCookieStore();
            return result;
        }
    }
    
    展开全文
  • 这个接口自动化测试框架到目前为止,我们已经完成了Get请求的封装和必要的工具类的支持。...所以,这个Java接口自动化测试框架的核心就是Get和POST请求方法的封装过程。 1.POST接口举例浏览器打开https://re...

           这个接口自动化测试框架到目前为止,我们已经完成了Get请求的封装和必要的工具类的支持。接下来这篇,我来介绍如何完成POST请求的封装过程。一般来说,在一个项目中,接口测试很多时候就是测试Get和POST方法,其他的请求方式的接口很少,占的比重几乎不计。所以,这个Java接口自动化测试框架的核心就是Get和POST请求方法的封装过程。

     

    1.POST接口举例

    浏览器打开https://reqres.in/,下拉一屏。点击第一个POST请求,这个接口的介绍信息如下。

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

     

    2.Postman手动实现

     

    我们先在本地postman环境,先来手动测试实现下这个post接口的请求过程。

    这个post接口请求还是比较简单,很容易在postman上实现该请求。

     

    3.Java代码自动化实现

     

           我们已经可以正确地在postman上实现创建用户这个接口的手动测试,那么我们想要这个过程自动化实现,如何做呢。下面我在RestClient.java封装了两个方法,一个是带请求头信息的Get请求,一个是带请求头信息的POST请求方法。这篇,了解了POST请求方法,带请求头的Get方法封装就很好理解。

    package com.qa.restclient;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    
    public class RestClient {
        
        
        //1. Get 请求方法
        public CloseableHttpResponse get(String url) throws ClientProtocolException, IOException {
            
            //创建一个可关闭的HttpClient对象
            CloseableHttpClient httpclient = HttpClients.createDefault();
            //创建一个HttpGet的请求对象
            HttpGet httpget = new HttpGet(url);
            //执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
            CloseableHttpResponse httpResponse = httpclient.execute(httpget);
            
            return httpResponse;
        }
        
        //2. Get 请求方法(带请求头信息)
        public CloseableHttpResponse get(String url,HashMap<String,String> headermap) throws ClientProtocolException, IOException {
                
            //创建一个可关闭的HttpClient对象
            CloseableHttpClient httpclient = HttpClients.createDefault();
            //创建一个HttpGet的请求对象
            HttpGet httpget = new HttpGet(url);
            //加载请求头到httpget对象
            for(Map.Entry<String, String> entry : headermap.entrySet()) {
                httpget.addHeader(entry.getKey(), entry.getValue());
            }
            //执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
            CloseableHttpResponse httpResponse = httpclient.execute(httpget);
                
            return httpResponse;
        }
        
        //3. POST方法
        public CloseableHttpResponse post(String url, String entityString, HashMap<String,String> headermap) throws ClientProtocolException, IOException {
            //创建一个可关闭的HttpClient对象
            CloseableHttpClient httpclient = HttpClients.createDefault();
            //创建一个HttpPost的请求对象
            HttpPost httppost = new HttpPost(url);
            //设置payload
            httppost.setEntity(new StringEntity(entityString));
            
            //加载请求头到httppost对象
            for(Map.Entry<String, String> entry : headermap.entrySet()) {
                httppost.addHeader(entry.getKey(), entry.getValue());
            }
            //发送post请求
            CloseableHttpResponse httpResponse = httpclient.execute(httppost);
            return httpResponse;
        }
        
        
    }
    
    
    

          然后,我们需要写一个TestNG测试用例来测试下这个封装的post方法好不好用。由于我们去前面几篇文章介绍了TestNG测试get方法的代码,这里我们就直接拷贝和修改部分代码就行。

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

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

    package com.qa.data;
    
    public class Users {
    
    	private String name;
    	private String job;
    	
    	public Users() {
    		super();
    	}
    
    	public Users(String name, String job) {
    		super();
    		this.name = name;
    		this.job = job;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public String getJob() {
    		return job;
    	}
    
    	public void setJob(String job) {
    		this.job = job;
    	}
    	
    }
    

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

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

          建议,在写测试用例过程中,需要和postman上的请求结果参考,特别是Headers这里的键值对。这里留一个作业,上面我写了一个Get带请求头的封装方法,你可以对照postman中的请求头去写一个单元测试用例去测试下这个带headers的Get方法。

           目前,Java接口自动化测试框架的核心部分,http请求方法的封装已经完成。接下里还有测试日志,测试报告,或者其他需要抽取优化的模块去完成。




    展开全文
  • Java接口自动化测试框架设计-2-Get请求方法和测试

    万次阅读 多人点赞 2018-05-22 00:11:42
    这篇开始,我来介绍通过代码逐步实现接口自动化测试框架的设计过程。先不要着急,框架设计我们只是介绍基本的组件,而且框架设计没有想象那么难,一步一步跟着做就会了。这篇我们来演示,如果通过Java代码来实现一个...

          这篇开始,我来介绍通过代码逐步实现接口自动化测试框架的设计过程。先不要着急,框架设计我们只是介绍基本的组件,而且框架设计没有想象那么难,一步一步跟着做就会了。这篇我们来演示,如果通过Java代码来实现一个用纯代码实现Http中的Get请求过程。

     

    1.Get请求API举例

          浏览器打开网址https://reqres.in,然后下拉一屏,我们就可以看到这个网站的API举例,我们来看看显示用户的get接口。


    通过这个图,我们能够获取这些信息

    1)网站host地址:https://reqres.in/

    2)用户展示请求方式是: Get

    3)接口的url 是: /api/users

    4)接口的响应状态码是200,还可以看到响应body的JSON内容。

     

           获取上面这些接口信息之后,我们在本地postman上来测试一下,如果没有postman请安装一个和postman差不多的能做接口手工测试的图形化界面工具,例如jmeter等。


          这个接口,我们通过postman手动测试,发现和网站提供的是一样结果,说明这get请求的接口测试通过。那么如果我们想通过Java代码实现,需要怎么做呢?接下来,才是本篇的重点内容。

     

    2.代码过程

          前面一篇文章,我们介绍了基础环境的搭建过程,这里,接着前面的环境来逐步完成一个Get请求的设计和测试过程。

    2.1 设计配置文件

          我们这个序列是要教会大家设计接口自动化测试框架的目的,所以我们一些设计和组织项目结构的方式,需要参考框架的思维。写一个配置文件,很简单,意义就是方便测试多套环境下的接口测试。我们工作中,一个项目,分测试环境,预发布环境和线上生产环境,这三套环境,接口肯定是一样,只不过服务器地址不同,所以,我们框架设计需要支持写一套接口测试用例,在三套环境上可以跑得同。

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

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

    TestBase.java 代码如下:

    package com.qa.base;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.Properties;
    
    public class TestBase {
    	
    	public Properties prop;
    	
    	//写一个构造函数
    	public TestBase() {
    		
    		try {
    			prop = new Properties();
    			FileInputStream fis = new FileInputStream(System.getProperty("user.dir")+
     "/src/main/java/com/qa/config/config.properties");
    			prop.load(fis);
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    	}
    	
    	
    }
    

          这里来复习下构造函数的作用,上面我们把加载配置文件的代码写在空参构造里,好处就是,每初始化这个类的对象就会执行构造函数的代码,即执行读取配置文件这么一个作用。关于上面配置文件路径是否拼接正确,你可以新建一个main方法的类,执行打印语句:System.out.println(System.getProperty("user.dir"));

    目前,项目结构图如下

    2.2 Get请求方法代码实现

          在src/main/java下新建一个包:com.qa.restclient,然后新建一个RestClient.java文件,下面是具体代码,实现了get请求,和得到相应状态码和响应头信息,以及响应主体的json内容。

    package com.qa.restclient;
    
    import java.io.IOException;
    import java.util.HashMap;
    import org.apache.http.Header;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    
    public class RestClient {
    	
    	
    	//1. Get 请求方法
    	public void get(String url) throws ClientProtocolException, IOException {
    		
    		//创建一个可关闭的HttpClient对象
    		CloseableHttpClient httpclient = HttpClients.createDefault();
    		//创建一个HttpGet的请求对象
    		HttpGet httpget = new HttpGet(url);
    		//执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
    		CloseableHttpResponse httpResponse = httpclient.execute(httpget);
    		
    		//拿到Http响应状态码,例如和200,404,500去比较
    		int responseStatusCode = httpResponse.getStatusLine().getStatusCode();
    		System.out.println("response status code -->"+responseStatusCode);
    		
    		//把响应内容存储在字符串对象
    		String responseString = EntityUtils.toString(httpResponse.getEntity(),"UTF-8");
    		
    		//创建Json对象,把上面字符串序列化成Json对象
    		JSONObject responseJson = JSON.parseObject(responseString);
    		System.out.println("respon json from API-->" + responseJson);
    		
    		//获取响应头信息,返回是一个数组
    		Header[] headerArray = httpResponse.getAllHeaders();
    		//创建一个hashmap对象,通过postman可以看到请求响应头信息都是Key和value得形式,所以我们想起了HashMap
    		HashMap<String, String> hm = new HashMap<String, String>();
    		//增强for循环遍历headerArray数组,依次把元素添加到hashmap集合
    		for(Header header : headerArray) {
    			hm.put(header.getName(), header.getValue());
    		}
    		
    		//打印hashmap
    		System.out.println("response headers -->"+ hm);
    		
    	}
    
    }
    

          上面的get方法代码写得比较乱,需要认真看注释,不然层次不清晰。这段代码肯定需要以后重构的,我们刚开始,方便我们测试就先这样去写就好。目前,项目结构图如下

    2.3 TestNG用例测试Get方法

          在src/test/java下新建一个包:com.qa.tests,然后新建一个GetApiTest.java类,写一个TestNG的测试用例来测试下我们上面写的Get请求方法。

    package com.qa.tests;
    
    import java.io.IOException;
    
    import org.apache.http.client.ClientProtocolException;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import com.qa.base.TestBase;
    import com.qa.restclient.RestClient;
    
    public class GetApiTest extends TestBase{
    	TestBase testBase;
    	String host;
    	String url;
    	RestClient restClient;
    	
    	
    	@BeforeClass
    	public void setUp() {
    		testBase = new TestBase();
    		host = prop.getProperty("HOST");
    		url = host + "/api/users";
    		
    	}
    	
    	@Test
    	public void getAPITest() throws ClientProtocolException, IOException {
    		restClient = new RestClient();
    		restClient.get(url);
    	}
    }
    

    选择run as testng,运行,输出结果如下:

    [RemoteTestNG] detected TestNG version 6.14.3
    response status code -->200
    respon json from API-->{"per_page":3,"total":12,"data":[{"last_name":"Bluth","id":1,"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg","first_name":"George"},{"last_name":"Weaver","id":2,"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg","first_name":"Janet"},{"last_name":"Wong","id":3,"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg","first_name":"Emma"}],"page":1,"total_pages":4}
    response headers -->{Transfer-Encoding=chunked, Server=cloudflare, CF-RAY=41e822894b39336d-HKG, Access-Control-Allow-Origin=*, ETag=W/"1bb-D+c3sZ5g5u/nmLPQRl1uVo2heAo", Connection=keep-alive, Set-Cookie=__cfduid=d9d93dc43c046707f916670ef491f4c8e1526917157; expires=Tue, 21-May-19 15:39:17 GMT; path=/; domain=.reqres.in; HttpOnly, Date=Mon, 21 May 2018 15:39:17 GMT, Content-Type=application/json; charset=utf-8, X-Powered-By=Express, Expect-CT=max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"}
    PASSED: getAPITest
    

    接下来,我们把响应内容的的bodyjson内容拷贝到一个在线的Josn格式化显示网站(https://www.json.cn/),看看效果如下。

          这个结果和postman上一样,数据都对得上,本篇关于Java代码基于Httpclient开源库实现Get请求的过程就介绍到这里。




    展开全文
  • 手把手教你搭建java接口自动化测试框架(三):基础代码填充 手把手教你搭建java接口自动化测试框架(三):基础代码填充 base包下新建TestBase.java package com.qa.base; import java.io.FileInputStream; ...

    手把手教你搭建java接口自动化测试框架(三):基础代码填充

    base包下新建TestBase.java

    package com.qa.base;
    
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.Properties;
    public class TestBase {
        //新建测试基类 此类为所有测试类的父类
        public Properties pro;
        //读取配置文件  把读取配置文件的操作卸载构造方法中  我也不知道为什么(摊手 可能这样 效率比较高
        public TestBase()  {
            pro = new Properties();
            try {
                FileInputStream fis = new FileInputStream(System.getProperty("user.dir")//获取当前工程目录
                                          + "/src/main/com/qa/config/config.properties");//获取config.properties文件目录
                pro.load(fis);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    config包下新建config.properties文件

    #项目的根url
    HOST = https://reqres.in
    
    #测试数据excel地址   进阶版本  数据驱动接口测试会用到  我本人目前是不会(lll¬ω¬)
    #postdata = xxxxx
    #getdata = xxxx
    

    https://reqres.in是一个提供免费接口调用的网站

    restClient包下新建ResClient.java

    package com.qa.restClient;
    
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.*;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    public class RestClient {
        //本类中包含post get put delete请求方法
        //1 get请求 不带请求头
        /**
         *
         * @param url  请求地址
         * @return
         * @throws IOException
         */
        public CloseableHttpResponse getApi(String url) throws IOException {
            //新建一个可关闭的HTTPclient对象
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //新建get对象
            HttpGet get = new HttpGet(url);
    
            //执行 get请求  存储返回的响应
            CloseableHttpResponse httpResponse = httpClient.execute(get);
            return httpResponse;
        }
        //2 get请求 带有请求头 方法重载
        public CloseableHttpResponse getApi(String url , HashMap<String,String> headermap) throws IOException {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);
            //加载请求头到HTTP中
            for (Map.Entry<String, String> entry : headermap.entrySet()) {
                httpGet.addHeader(entry.getKey(),entry.getValue());
            }
            CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
            return httpResponse;
        }
        /**
         *
         * @param url
         * @param entityString 设置json 请求参数
         * @param headermap 请求头
         * @return
         * @throws IOException
         */
        //3 post请求
        public CloseableHttpResponse postApi(String url, String entityString, HashMap<String,String> headermap) throws IOException {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            //设置payload
            httpPost.setEntity(new StringEntity(entityString));
            //加载请求头对象到 httppost
            for (Map.Entry<String,String> entry : headermap.entrySet()){
                httpPost.addHeader(entry.getKey(),entry.getValue());
            }
            //发送post请求
            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            return httpResponse;
        }
        //4 Put方法
        public CloseableHttpResponse put(String url, String entityString, HashMap<String,String> headerMap) throws ClientProtocolException, IOException {
    
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpPut httpput = new HttpPut(url);
            httpput.setEntity(new StringEntity(entityString));
            for(Map.Entry<String, String> entry : headerMap.entrySet()) {
                httpput.addHeader(entry.getKey(), entry.getValue());
            }
            //发送put请求
            CloseableHttpResponse httpResponse = httpclient.execute(httpput);
            return httpResponse;
        }
        //5 Delete方法
        public CloseableHttpResponse delete(String url) throws ClientProtocolException, IOException {
    
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpDelete httpdel = new HttpDelete(url);
            //发送put请求
            CloseableHttpResponse httpResponse = httpclient.execute(httpdel);
            return httpResponse;
        }
    }
    

    到这一步呢请求方法就基本完成了 加上请求内容
    就可以进行接口测试了
    是不是很想试一试(~ ̄▽ ̄)~

    请看下集👇

    展开全文
  • 自动化测试领域内流传着一个说法:单元测试才是自动化测试的核心,在自动化测试里,无论框架何等完美都不可能脱离单元测试,单元测试将会是自动化测试里最小的单位,把它看作单位一,若干个单位一组成了一个整体,...
  • Rest Assured(API接口自动化测试框架体系) 现在,越来越多的 Web 应用转向了 RESTful 的架构,很多产品和应用暴露给用户的往往就是一组 REST API,这样有一个好处,用户可以根据需要,调用不同的 API,整合出自己...
  • 手把手教你搭建java接口自动化测试框架(一):前期准备工作 本人也是接口自动化测试框架小白 最近跟着一些教程成功搭建了自己的接口自动化测试框架 该框架基于IDEA+TestNG+extentreport可完成基本的post、get请求 ...
  • javaWeb--API 自动化测试框架分享

    千次阅读 2015-11-04 12:29:54
    目前在做接口测试方面的工作,结合部门现状,初步整理及搭建了api自动化测试框架,现在把我的思路、框架结构和大家分享出来,一方面希望可以为大家提供一些参考,另一方面也希望大家多提意见,以便测试框架的改进~...
  • Java接口自动化测试框架学习(三)

    万次阅读 多人点赞 2018-09-14 16:53:32
    前面项目已创建好,依赖包添加完成,testng也已添加 项目结构如下: 1.设计配置文件 在src/main/java下新建一个包:...然后在src/main/java下新建一个包:com.qa.base,新建一个TestBase.java,这个...
  • 手把手教你搭建java接口自动化测试框架(四):断言、生成测试报告 上一集说到post和Get请求,请求后得到的响应(即接口返回值)是我们想要的吗 比如网站上get接口文档说明 : “data”: [ { “id”: 7, “email”: ...
  • 手把手教你搭建java接口自动化测试框架(四):get、post方法实现 看完了三篇 各种配置、代码 到这一篇终于可以进行实战了 是骡子是马拉出来溜溜( •̀ ω •́ )✧ 执行get请求 tests包下新建GetTest01.java ...
  • 前面说过了,如果你稍微懂得TestNG这个单元测试框架,到目前这个简单的Java接口自动化测试框架主体的骨架部分已经完成设计并实现。这篇,继前篇的基础上,把测试用例中获取响应状态码和响应数据转换成JSON格式这些...
  • 本文来自于个人微博,在这篇文章中,我们将介绍一下开源的Web-API自动化测试框架——Karate. 在这篇文章中,我们将介绍一下开源的Web-API自动化测试框架——Karate Karate是基于另一个BDD测试框架Cucumber来建立的,...
  • Java接口自动化测试框架学习(二)

    千次阅读 2018-08-16 13:06:12
    1.关于接口方面API的学习,这样才能在代码中熟练使用 httpclient:https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/ ...
  • Java+webdriver的自动化测试框架搭建

    千次阅读 2018-09-28 10:42:54
     webdriver是一种支持web应用程序自动化测试的工具,利用浏览器原生的API,封装成一套更加面向对象的selenium webdriver API,直接操作浏览器里的元素。除了必要的浏览器驱动外,webdriver不需要启动其他任何程序,...
  • 怎么了 项目使用TestNG框架并具有Maven性质 com.hellofresh.api.test中有4个测试 com.hellofresh.api.model中提供的服务响应数据模型
  • 【自动化测试】自动化测试框架与工具

    千次阅读 多人点赞 2021-05-06 21:01:01
    文章目录1)什么是自动化测试框架?1.1 什么是框架?1.2 什么是自动化测试框架?1.2.1 优点:1.2.2 框架的基本组件1、需要配置文件管理:2、业务逻辑代码和测试脚本分离3、报告和日志文件输出4、自定义的库的封装5、...
  • 【附源码】测试框架总体来说与应用开发的框架并无太大的差异,在设计的起始阶段初衷也都一样,满足测试绝大多数活动并提高测试代码的编写效率,然后兼顾易用、兼容、通用以及简单维护等几个维度是其存在的唯一意义,...
  • LazyAndroid是为了解决安卓UI自动化测试中存在的测试工具学习成本高、测试编码中花费在元素查找中的时间过长等问题而诞生的一款UI自动化测试框架。它基于appium,封装了appiumDriver的设置、安卓基本控件的使用和...
  • 接着上面一篇,这篇来封装下PUT和Delete方法。...由于弄明白了前面的Get和Post方法封装过程,现在就直接贴出封装方法和测试代码。1.PUT和Deletet方法package com.qa.restclient; import java.io.IOExcepti...
  • 前面一篇,我们介绍了Get方法的设计过程和测试结果,现在我们需要对前面代码进行重构和修改,本篇需要完成以下目标。1)重构Get方法2)如何进行JSON解析3)使用TestNG方法进行测试断言 1.重构Get方法 在前面文章,...
  • 【附源码】关键字驱动也称为表格驱动或者操作名驱动,它有绝对的优势比如不需要太多的技术,一旦框架建立,手工测试人员和非技术人员都可以很容易的编写自动化测试脚本;简单易懂:它存在Excel表格中,没有编码,...
  • 在这个自动化测试框架中。 在config目录中存放的是测试配置相关的文件,配置文件可以使用ini、xml、yml等文件类型。例如,要测试的网址、调试日志的文件名、日志的输出格式等 在data目录中存放的是需要测试的数据。...
  • 5种比较常见的自动化测试框架

    千次阅读 2020-10-26 14:34:43
    自动化测试框架
  • WeTest是处理API接口测试的轻量级自动化测试框架java语言实现,拓展JUnit4开源框架,支持Ant/Maven执行方式。 工具特点: 1.支持suite,根据JUnit4测试类名正则匹配,聚合相同模块的用例,运行单个聚合类即可实现...
  • 自动化 测试 框架 开源 测试自动化框架是一组最佳实践,通用工具和库,可帮助质量保证测试... 尽管团队可以构建复杂的自动化测试框架,但是当他们可以使用现有的开源工具,库和测试框架获得相同甚至更好的结果时,通...
  • Android测试之Robotium自动化测试框架

    万次阅读 2017-06-14 00:17:07
    Robotium是一款国外的自动化测试框架,是一款免费的Android UI测试工具,主要针对Android平台的应用进行黑盒自动化测试,它提供了模拟各种手势操作(点击、长按、滑动等)、查找和断言机制的API,能够对各种控件进行...
  • 本篇开始将介绍几个重量...Instrumentation从android2.3甚至更早版本就存在了,很多Android自动化测试框架都是对于Instrumentation的直接使用或二次开发,例如:Robotium。在学习Instrumentation之前,先了解一下Junit,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 124,022
精华内容 49,608
关键字:

javaapi自动化测试框架

java 订阅