zoukankan      html  css  js  c++  java
  • 在自动化测试中使用TestNG【第 4 章 接口自动化测试 4.5 TestNG 集成 HttpClient】

    4.5 TestNG 集成 HttpClient

    首先将 TestNG 和 HttpClient 进行集成,然后进行 HTTP 接口自动化测试

    在 pom.xml 文件的 <dependencies> 标签中输入以下粗体部分内容

    <?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">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>cn.edu.bjut</groupId>
    <artifactId>httpinterfacetest</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20180813</version>
        </dependency>
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
    </dependencies>
    
    </project>

    保存 pom.xml 文件,这时 Maven 会自动下载依赖的 jar 包。这里添加了 4 个依赖

    ①TestNG:自动化测试框架 TestNG 的依赖

    ②JSON:用于处理 JSON 数据,在 RESTful POST 请求的请求体及响应体中均可使用

    ③DOM4J:用于处理 XML 数据,在 SOAP 请求的请求体及响应体中均可使用

    ④JAXEN:当 DOM4J 用 XPath 方式获取节点时需要依赖该 jar 包

    依赖 jar 包下载完成后,在工程(httpinterfacetest)上用鼠标右击,从弹出的快捷菜单中选择「TestNG → Convert to TestNG」选项,在工程中生成 testng.xml 文件

    4.5.1 RESTful 接口自动化测试

    1.编写 GET 接口自动化测试用例

     

    把 Test Class 重命名为 GetMobilePhoneTest,删除 GetMobilePhoneTest 中的内容,输入以下代码

    package cn.edu.bjut;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.testng.Assert;
    import org.testng.annotations.AfterClass;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import java.net.URI;
    
    public class GetMobilePhoneTest {
        private CloseableHttpClient client;
        private CloseableHttpResponse response;
    
        @BeforeClass
        public void init() {
            client = HttpClients.createDefault();
        }
    
        @Test
        public void testCase1() {
            Assert.assertEquals("{"brand":"Apple","model":"iPhone 6S","os":"IOS"}",
                    sendHttpGetRequest(client, "iPhone 6S"));
        }
    
        @Test
        public void testCase2() {
            Assert.assertEquals("", sendHttpGetRequest(client, ""));
        }
    
        @Test
        public void testCase3() {
            Assert.assertEquals("", sendHttpGetRequest(client, null));
        }
    
        @Test
        public void testCase4() {
            Assert.assertEquals("", sendHttpGetRequest(client, "01234567890123456789012345678901234567890123456789"));
        }
    
        @AfterClass
        public void clear() {
            try {
                response.close();
                client.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        //定义一个私有的方法,用来发送Get请求
        private String sendHttpGetRequest(CloseableHttpClient client, String model) {
            String result = null;
            try {
                URI uri = new URIBuilder().setScheme("http").setHost("localhost").setPort(8080).setPath("/mobilePhone")
                        .setParameter("model", model).build();
                response = client.execute(new HttpGet(uri));
                result = EntityUtils.toString(response.getEntity());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }

    修改 testng.xml 文件,在&lt;test&gt;标签中新增以下粗体部分内容

    保存所做的修改,在 testng.xml 上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Suite」选项,可以看到如图 4-2 所示的测试报告

    图 4-2

    下面对运行结果进行说明

    ①init()方法初始化了一个 HTTP 客户端,并在 sendHttpGetRequest()方法中使用,最后在 clear()方法中关闭

    ② 将构造 URI、发送 GET 请求和转换服务器响应等过程封装在 sendHttpGetRequest()方法中,通过传递 HTTP 客户端和参数来使用该方法

    ③ 在 clear()方法中对 HTTP 客户端和服务器响应进行关闭操作,在操作过程中可能出现异常,所以需要对异常进行捕获和处理

    2.POST 接口自动化测试用例编写

    在 cn.edu.bjut.httpinterfacetest Package 中创建名为 SaveMobilePhoneTest 的 Class,在 SaveMobilePhoneTest 中输入以下内容

     

     

     

    修改 testng.xml 文件,修改&lt;class&gt;标签中的 name 属性值,见以下粗体部分内容

     

    保存所做的修改,在 testng.xml 上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Suite」选项,然后查看测试报告,如图 4-3 所示

    图 4-3

    下面对运行结果进行说明

    ① 整体思路和上一个示例一致,只是将 sendHttpGetRequest()方法改成了 sendHttpPostRequest()方法,具体的实现都封装到了 sendHttpPostRequest()方法中

    ② 从测试报告可以看出,有 7 个测试用例执行失败了,这时我们可以查看失败原因,以 testCase11 为例,单击「testCase11,如图 4-4 所示

    图 4-4 

    复制 expected 后的方括号中的内容

     

    可以看到服务器返回了 500 错误,提示没有「SYMBIAN」这个枚举,这里表明了待测程序设计不够严谨。待测程序没有对入参进行校验,导致不能返回友好的提示,而是直接返回了服务器 500 错误

    实际项目中,自动化测试用例执行失败后,通过对结果进行分析,在确认失败原因为待测程序的缺陷时,就应该将缺陷提交给开发人员

    3.简化自动化测试用例

    上例中有 11 个自动化测试用例,但通过观察可以发现入参和期望值很相似,因此代码看上去很冗余,有没有简化的方法呢?答案是肯定的。第 3 章曾介绍过数据分离,这里就派上用场了。删除 SaveMobilePhoneTest 中的内容,输入以下内容

    保存代码,选择「Run As → TestNG Suite」选项,测试报告如图 4-5 所示 

     

    图 4-5

    这里使用了@DataProvider 注解修饰一个方法,该方法用于提供测试数据,测试数据包括期望值和请求的实体字符串

    4.解析 JSON 字符串

    RESTful 接口通过 JSON 传输数据,以上的例子我们都将 JSON 字符串当作「一般字符串」对待了,实际上可以通过对 JSON 字符串的解析进行更细粒度的控制。作为示例讲解,这里删除了 testCase2~testCase11,仅保留 testCase1

    package cn.edu.bjut;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.json.JSONObject;
    import org.testng.Assert;
    import org.testng.annotations.AfterClass;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    public class SaveMobilePhoneTest {
        private CloseableHttpClient client;
        private CloseableHttpResponse response;
    
        @BeforeClass
        public void init() {
            client = HttpClients.createDefault();
        }
    
        @Test
        public void testCase1() {
            JSONObject expected = new JSONObject().put("code", 0).put("message", "保存成功!");
            JSONObject stringEntity = new JSONObject().put("brand", "Motorola").put("model", "moto Z Play").put("os",
                    "ANDROID");
            JSONObject actual = sendHttpPostRequest(client, stringEntity.toString());
            if (!(expected.get("code") == actual.get("code")) || !(expected.get("message").equals(actual.get("message")))) {
                Assert.fail("失败!");
            }
        }
    
        @AfterClass
        public void clear() {
            try {
                response.close();
                client.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private JSONObject sendHttpPostRequest(CloseableHttpClient client, String stringEntity) {
            JSONObject result = null;
            try {
                HttpPost httpPost = new HttpPost("http://localhost:8080/mobilePhone");
                httpPost.setEntity(new StringEntity(stringEntity, ContentType.APPLICATION_JSON));
                response = client.execute(httpPost);
                result = new JSONObject(EntityUtils.toString(response.getEntity()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }

    在 testCase1 中,将预期结果、入参和实际结果都用 JSONObject 对象表示,包括断言也是通过操作 JSONObject 对象完成的,整个过程看不到「糟心」的字符串。这样做的好处是将 RESTful 请求的请求体和响应体完全「JSON」化,便于进一步封装,并且与字符串也实现了「解耦

    4.5.2 SOAP 接口自动化测试

    1.自动化测试用例编写

    在 cn.edu.bjut.httpinterfacetest Package 中创建名为 GetMobilePhoneSoapTest 的 Class,在 GetMobilePhoneSoapTest 中输入以下内容

    修改 testng.xml 文件,修改&lt;class&gt;标签中的 name 属性值,见以下粗体部分内容

    保存所做的修改,在 testng.xml 上用鼠标右击,从弹出的快捷菜单中选择「Run As → TestNG Suite」选项,测试报告如图 4-6 所示

    图 4-6

    测试用例均通过测试,与预期一致

     

    2.解析 XML 字符串

    直接处理 XML 字符串虽然能解决问题,但是代码可读性很差。这里使用 DOM4J 处理 XML 字符串。作为示例讲解,这里仅保留了 testCase1

    下面对代码进行说明

    GetMobilePhoneSoapTest.java

    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.dom4j.Document;
    import org.dom4j.DocumentHelper;
    import org.dom4j.Element;
    import org.dom4j.XPath;
    import org.testng.Assert;
    import org.testng.annotations.AfterClass;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    public class GetMobilePhoneSoapTest {
        private CloseableHttpClient client;
        private CloseableHttpResponse response;
    
        @BeforeClass
        public void init() {
            client = HttpClients.createDefault();
        }
    
        @Test
        public void testCase1() {
            // 构建期望XML
            Document expected = DocumentHelper.createDocument();
            Element root = expected.addElement("SOAP-ENV:Envelope").addAttribute("xmlns:SOAP-ENV",
                    "http://schemas.xmlsoap.org/soap/envelope/");
            root.addNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
            root.addElement("SOAP-ENV:Header");
            Element body = root.addElement("SOAP-ENV:Body");
            Element node = body.addNamespace("ns2", "http://www.lujiatao.com/httpinterface/MobilePhones")
                    .addElement("ns2:getMobilePhoneResponse")
                    .addAttribute("xmlns:ns2", "http://www.lujiatao.com/httpinterface/MobilePhones");
            Element nodeChild = node.addElement("ns2:mobilePhone");
            nodeChild.addElement("ns2:brand").addText("Apple");
            nodeChild.addElement("ns2:model").addText("iPhone 6S");
            nodeChild.addElement("ns2:os").addText("IOS");
            // 构建期望XML对应的XPath
            Map<String, String> xmlMap = new HashMap<>();
            xmlMap.put("ns2", "http://www.lujiatao.com/httpinterface/MobilePhones");
            XPath xPath1 = expected.createXPath("//ns2:brand");
            XPath xPath2 = expected.createXPath("//ns2:model");
            XPath xPath3 = expected.createXPath("//ns2:os");
            xPath1.setNamespaceURIs(xmlMap);
            xPath2.setNamespaceURIs(xmlMap);
            xPath3.setNamespaceURIs(xmlMap);
            // 构建入参XML
            Document soapString = DocumentHelper.createDocument();
            Element root2 = soapString.addElement("SOAP-ENV:Envelope")
                    .addAttribute("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/")
                    .addAttribute("xmlns:hi", "http://www.lujiatao.com/httpinterface/MobilePhones");
            root2.addNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
            root2.addNamespace("hi", "http://www.lujiatao.com/httpinterface/MobilePhones");
            root2.addElement("SOAP-ENV:Header");
            Element body2 = root2.addElement("SOAP-ENV:Body");
            Element node2 = body2.addElement("hi:getMobilePhoneRequest");
            node2.addElement("hi:model").addText("iPhone 6S");
            // 接收响应XML并断言
            Document actual = sendHttpPostRequest(client, soapString.asXML());
            if (!(xPath1.selectSingleNode(expected).getText().equals(xPath1.selectSingleNode(actual).getText()))
                    || !(xPath2.selectSingleNode(expected).getText().equals(xPath2.selectSingleNode(actual).getText()))
                    || !(xPath3.selectSingleNode(expected).getText().equals(xPath3.selectSingleNode(actual).getText()))) {
                Assert.fail("失败!");
            }
        }
    
        @AfterClass
        public void clear() {
            try {
                response.close();
                client.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private Document sendHttpPostRequest(CloseableHttpClient client, String soapString) {
            Document result = null;
            try {
                HttpPost httpPost = new HttpPost("http://localhost:8080/MobilePhones");
                httpPost.setEntity(new StringEntity(soapString, ContentType.TEXT_XML));
                response = client.execute(httpPost);
                result = DocumentHelper.parseText(EntityUtils.toString(response.getEntity()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }
    View Code

    ① 使用 DOM4J 中的 API 构建期望值,实际上一个是 Document 对象

    ② 后续断言会使用 XPath 方式查找期望值和实际值中的指定数据,因此这里先构建 XPath

    ③ 使用 DOM4J 中的 API 构建入参

    ④ 首先接收实际值(也是一个 Document 对象,然后通过 XPath 获得节点,最后通过对节点的文本进行比较完成断言

  • 相关阅读:
    对初学者的几点建议
    关于.net的一些资源网站
    iis 经常出现的问题以及解决方案
    C#编程规范(2008年4月新版)
    优秀ASP.NET程序员的修炼之路
    网站软件开发规范(某门户网站的)
    asp.net跳转页面的三种方法比较
    H5开发相关资料
    C#简介
    用C#获取硬盘序列号,CPU序列号,网卡MAC地址
  • 原文地址:https://www.cnblogs.com/MarlonKang/p/14218672.html
Copyright © 2011-2022 走看看