在自动化测试中使用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 获得节点,最后通过对节点的文本进行比较完成断言

原文地址:https://www.cnblogs.com/MarlonKang/p/14218672.html