Moco+HttpClient+TestNg+MyBatis+Mysql接口測試框架

接口框架實現(xiàn)整體步驟:

resource層:
底層是application.properties配置路徑文件
databaseconfig.xml配置數據庫文件
mapper文件夾中是sqlmapper.xml的配置文件
testng.xml配置文件 測試類的配置文件
還有moco的接口文件,json格式的,需要一個jar包依賴

utils層:
configfile類是從application.properties配置文件中獲取url和參數信息,string、json、map等類型
databaseutil類是加載database.xml文件,處理從數據庫中獲取數據信息

model層:
對應的實體類是與數據庫的表一致、還可以定義接口名的枚舉類和參數url的枚舉類

config層:
配置測試報告,extenttestngrepoter監(jiān)聽器類

case層:
寫具體的接口類,用httpclient去實現(xiàn)每個接口的調用和結果驗證
接口與接口之間的數據依賴可以通過testng的數據依賴去傳遞。

一、創(chuàng)建maven項目,配置pom文件

        <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>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</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.9.10</version>
        </dependency>

二、創(chuàng)建application.properties文件

test.url=http://localhost:8086

#登錄接口
login.uri=/demo/login
#更新用戶信息接口
updateUserInfo.uri=/demo/updateUserInfo
#獲取用戶列表接口
getUserList.uri=/demo/getUserInfo
#獲取用戶信息接口
getUserInfo.uri=/demo/getUserInfo
#添加用戶接口
addUser.uri=/demo/addUser

三、創(chuàng)建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://localhost:3306/dbgirl"/>
                <!-- 數據庫用戶... -->
                <property name="username" value="rootname"/>
                <!-- 數據庫密碼... -->
                <property name="password" value="passname"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 注冊映射文件:java對象與數據庫之間的xml文件路徑! -->
    <mappers>
        <mapper resource="mapper/SQLMapper.xml"/>
    </mappers>
</configuration>

四、在resource下創(chuàng)建mapper文件夾,創(chuàng)建SQLMapper.xml文件,先不寫內容
testng.xml文件也可以先建立


image.png

五、利用springboot中的spring-data-jpa創(chuàng)建實體類,創(chuàng)建數據庫表,可以參考在《springboot學習(四)》中的代碼。
也可以手動去創(chuàng)建數據庫表。
六、在java下創(chuàng)建cases、config、model、utils文件夾,分別放不同的內容
現(xiàn)在model中創(chuàng)建對應數據庫表的實體類,代碼如下:
只舉其中一個表的例子,字段值要與數據庫表的值一致

package com.course.model;

import lombok.Data;

@Data
public class User {
    private Integer id;  //id是主鍵,并且會自增,調接口時不需要傳參數id,傳其他參數就可以
    private String userName;
    private String password;
    private Integer age;
    private String sex;
    private Integer permission;
    private Integer isDelete;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", permission=" + permission +
                ", isDelete=" + isDelete +
                '}';
    }
}

該代碼中沒有寫get和set方法,是因為用了lombok可以自動省略get和set方法

下面會用到logincase的model類,補充一下源碼:

package com.course.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "loginCase")
public class loginCase {
    @Id
    @GeneratedValue
    private Integer id;
    private String userName;
    private String password;
    private String expected;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getExpected() {
        return expected;
    }

    public void setExpected(String expected) {
        this.expected = expected;
    }
}

在model中寫一個枚舉類,把接口寫成枚舉類型,保證傳進來的接口名稱正確

package com.course.model;

public enum InterfaceName {
    LOGIN,UPDATEUSERINFO,GETUSERLIST,GETUSERINFO,ADDUSER
}

七、在config文件中創(chuàng)建ExtentTestNGIReporterListener類,測試報告會用到
Maven pom文件中添加依賴:

<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>

在resource中增加testng.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="用戶管理系統(tǒng)測試套件">
    <test name="用戶管理系統(tǒng)測試用例">
        <classes>
            <class name="com.course.cases.LoginTest">
                <include name="LoginTrue" />
                <include name="LoginFalse" />
            </class>
            <class name="com.course.cases.AddUserTest">
                <include name="AddUser" />
            </class>
            <class name="com.course.cases.GetUserInfoTest">
                <include name="getUserInfo" />
            </class>
            <class name="com.course.cases.GetUserListTest">
                <include name="getUserList" />
            </class>
            <class name="com.course.cases.updateUserInfo">
                <include name="updateUserInfo" />
            </class>
        </classes>

    </test>
    <listeners>
        <listener class-name="com.course.config.ExtentTestNGIReporterListener"/>
    </listeners>
</suite>

<listener class-name="com.course.config.ExtentTestNGIReporterListener"/>
中ExtentTestNGIReporterListener類的路徑,就是下面要在config文件夾下創(chuàng)建的ExtentTestNGIReporterListener類的路徑。

在config文件夾下增加監(jiān)聽類:不用改類中的內容:

package com.course.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;
            }
            //統(tǒng)計suite下的成功、失敗、跳過的總用例數
            int suiteFailSize=0;
            int suitePassSize=0;
            int suiteSkipSize=0;
            ExtentTest suiteTest=null;
            //存在多個suite的情況下,在報告中將同一個一個suite的測試結果歸為一類,創(chuàng)建一級節(jié)點。
            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){
                    //沒有創(chuàng)建suite的情況下,將在SuiteResult的創(chuàng)建為一級節(jié)點,否則創(chuàng)建為suite的一個子節(jié)點。
                    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());
                    //統(tǒng)計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() {
        //文件夾不存在的話進行創(chuàng)建
        File reportDir= new File(OUTPUT_FOLDER);
        if(!reportDir.exists()&& !reportDir .isDirectory()){
            reportDir.mkdir();
        }
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        // 設置靜態(tài)文件的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) {
        //存在父節(jié)點時,獲取父節(jié)點的標簽
        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{
                    //作為子節(jié)點進行創(chuàng)建時,設置同父節(jié)點的標簽一致,便于報告檢索。
                    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();
    }
}

要先把test-output文件夾展示出來,需要配置idea:
配置testng報告,進入idea,按著圖中去點擊


image.png

選擇左側測試方法,點擊listener,勾選Use default reporters,然后點擊確定


image.png

右鍵執(zhí)行testng.xml文件,就能看到test-output文件夾,查看生成的報告:


image.png

右鍵點擊index.html選擇copy path,粘貼路徑在瀏覽器中查看報告信息。

八、在utils文件中創(chuàng)建configFile,從配置文件中利用resourcebundle取url,代碼如下

package com.course.utils;

import com.course.model.InterfaceName;

import java.util.Locale;
import java.util.ResourceBundle;

public class configFile {
    //從application文件中取url
    private static ResourceBundle resourceBundle = ResourceBundle.getBundle("application", Locale.CHINA);

    public static String getUrl(InterfaceName name){
        //取域名和端口號
        String baseurl = resourceBundle.getString("test.url");
        String uri = "";
        //如果傳進來的接口名稱正確,就返回相應接口的url
        if(name == InterfaceName.LOGIN){
            uri = resourceBundle.getString("login.uri");
        }
        if(name == InterfaceName.UPDATEUSERINFO){
            uri = resourceBundle.getString("updateUserInfo.uri");
        }
        if(name == InterfaceName.GETUSERLIST){
            uri = resourceBundle.getString("getUserList.uri");
        }
        if (name == InterfaceName.GETUSERINFO){
            uri = resourceBundle.getString("getUserInfo.uri");
        }
        if(name == InterfaceName.ADDUSER){
            uri = resourceBundle.getString("addUser.uri");
        }

        //拼接url
        String testurl = baseurl + uri;
        return testurl;
    }
}


九、在utils中創(chuàng)建DatabaseUtil,從databaseconfig.xml配置文件中取配置信息,創(chuàng)建sqlsession并返回,在case類中就不用再寫獲取數據庫信息了

package com.course.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;
/*
把sqlsession封裝起來,從數據庫中獲取數據
 */
public class DatabaseUtil {
    public static SqlSession getsqlSession() throws IOException {
        //獲取配置的資源文件
        Reader reader = Resources.getResourceAsReader("databaseConfig.xml");
        //得到SqlSessionFactory,使用類加載器加載xml文件
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        //得到sqlsession對象,這個對象就能執(zhí)行配置文件中的sql語句啦
        SqlSession session = factory.openSession();

        return session;

    }
}

image.png

十、在cases文件夾下寫測試類,先寫一個loginTest

package com.course.cases;

import com.course.config.TestConfig;
import com.course.model.InterfaceName;
import com.course.model.loginCase;
import com.course.utils.DatabaseUtil;
import com.course.utils.configFile;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.ibatis.session.SqlSession;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;

public class LoginTest {

    //加@BeforeTest注解,在用例執(zhí)行前先獲取接口的url
    @BeforeTest(groups = "loginTrue",description = "登錄接口")
    public void beforeTest(){
        //獲取接口地址
        TestConfig.loginuri = configFile.getUrl(InterfaceName.LOGIN);
        TestConfig.addUseruri = configFile.getUrl(InterfaceName.ADDUSER);
        TestConfig.getUserInfouri = configFile.getUrl(InterfaceName.GETUSERINFO);
        TestConfig.getUserListuri = configFile.getUrl(InterfaceName.GETUSERLIST);
        TestConfig.updateUserInfouri = configFile.getUrl(InterfaceName.UPDATEUSERINFO);

        TestConfig.defaultHttpClient = new DefaultHttpClient();
    }
    @Test(groups = "loginTrue", description = "登錄成功的接口")
    public void LoginTrue() throws IOException {
        //定義sqlsession對象,從DatabaseUtil類中調getsqlSession方法,直接獲取到sqlsession對象,可以執(zhí)行sql語句,不需要配置數據庫信息
        SqlSession sqlSession = DatabaseUtil.getsqlSession();
        //調selectone方法,通過mybatis中的sql語句查數據庫表,并返回結果賦值給logincase對象
        //sqlstatement與SQLMapper.xml中的id一致,33是傳的參數id
        loginCase loginCase = sqlSession.selectOne("logincase",33);
        //打印查詢的結果,結果就是從數據庫中查詢到的結果
        System.out.println(loginCase.toString());
        System.out.println(TestConfig.loginuri);
    }

    //調數據庫表中第二行數據,可以認為是用戶名和密碼錯誤,登錄失敗的接口
    @Test(groups = "loginFalse",description = "登錄失敗的接口")
    public void LoginFalse() throws IOException {
        SqlSession sqlSession = DatabaseUtil.getsqlSession();
        loginCase loginCase = sqlSession.selectOne("logincase",36);
        System.out.println(loginCase.toString());
        System.out.println(TestConfig.loginuri);
    }

}

其中TestConfig.loginuri等屬性,是在config文件夾下定義了一個TestConfig的類,給該類創(chuàng)建了多個uri名稱的屬性,這樣uri名稱就不用隨便再起,直接從這個類中調用就行。

package com.course.config;

import org.apache.http.client.CookieStore;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestConfig {
    public static String loginuri;
    public static String updateUserInfouri;
    public static String getUserListuri;
    public static String getUserInfouri;
    public static String addUseruri;

    public static DefaultHttpClient defaultHttpClient;
    public static CookieStore cookieStore;
}

image.png

十一、執(zhí)行l(wèi)oginTest類時遇到問題:
報org.apache.ibatis.exceptions.PersistenceException:的錯誤
是因為mybatis沒有連接上數據庫,或者是數據庫和實體類的對應關系沒有建立起來
需要修改databaseconfig.xml文件,數據庫url為:

<property name="url" value="jdbc:mysql://localhost:3306/dbgirl?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8"/>

之前該url是寫在yml中,&符號不需要轉義,寫到xml中需要對 & 轉義成&

還需要加上
<typeAliases>
<typeAlias type="com.course.model.loginCase" />
<typeAlias type="com.course.model.addUserCase" />
</typeAliases>

要加到environments配置前面,要不然會報錯:
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHand......

<typeAlias type="com.course.model.addUserCase" /> 要寫多個,哪個實體類對應的有數據庫表都要單獨寫,要不然也會報錯。

同時mapper/SQLMapper.xml中的namespace也要寫對com.course.model

<?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.course.model">
    <select id="logincase" parameterType="Integer" resultType="com.course.model.loginCase">
            select * from loginCase where id=#{id}
    </select>
</mapper>

十二、HttpClient的學習總結:

以上的框架中缺少moco接口,請參考《Moco接口框架應用實戰(zhàn)》。
下面會在前十一章搭建框架的基礎上,利用HttpClient去調接口驗證測試。
12.1 moco的接口:
登錄接口


image.png

還有一個調列表接口,不再貼圖:

TestDologin 測試類:

public class TestDologin {


    //設置全局變量cookieStore
    public static CookieStore cookieStore;

//    //返回token值
//    public static String token;

    @BeforeTest(groups = "loginTrue")
    public void beforetest(){

        //獲取所有接口的uri
        TestConfig.doLoginuri = ConfigFile.GetUrl(InterfaceName.DOLOGIN);
        TestConfig.getMerchantListuri = ConfigFile.GetUrl(InterfaceName.GETMERCHANTLIST);
        //獲取所有接口的參數值,doLoginParams接口是post入參json格式的,可以從配置文件中調,getMerchantListuri中已拼接了接口
        TestConfig.doLoginParams = ConfigFile.GetParams(ParamsName.DOLOGINPARAMS);
    }
    
    /*
    從配置文件中獲取全部的參數轉換成String類型入參
     */
    @Test(groups = "loginTrue", description = "登錄成功的接口")
    public void testdologin() throws IOException {
        System.out.println(TestConfig.doLoginuri);
        System.out.println(TestConfig.doLoginParams);

        DefaultHttpClient httpClient = new DefaultHttpClient();
        //該接口返回的有cookie,獲取cookie,賦值給全局變量,下一個接口setcookiestore,下一個接口入參時使用cookie
        cookieStore = httpClient.getCookieStore();

        //調moco的接口可以用這個
        HttpPost httpPost = new HttpPost(TestConfig.doLoginuri);
        //設置參數
        //使用json入參,入參是中文時不行。

        //設置請求頭header
        httpPost.setHeader("Content-Type","application/json;charset=gbk");
        //傳入參數

        StringEntity entity=new StringEntity(TestConfig.doLoginParams.toString(),"gbk");
        httpPost.setEntity(entity);

        //獲取返回結果
        HttpResponse httpResponse = httpClient.execute(httpPost);
        String result = EntityUtils.toString(httpResponse.getEntity());
        System.out.println(result);

    }
}

TestgetMerchantList測試類:

package com.course.cases;

import com.course.config.TestConfig;
import com.course.utils.DatabaseUtil;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONArray;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import org.apache.http.client.CookieStore;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONObject;

public class TestgetMerchantList {

    public static int MerchantId;

    @Test(dependsOnGroups = "loginTrue",description = "獲取列表接口")
    public void TestgetMerchantList() throws URISyntaxException, IOException {
        System.out.println(TestConfig.getMerchantListuri);

        DefaultHttpClient httpClient = new DefaultHttpClient();

        //參數已經拼接在url中
        HttpGet httpGet = new HttpGet(TestConfig.getMerchantListuri);



        //通過moco接口,可以使用setcookiestore方法從上一個接口獲取token值
        httpClient.setCookieStore(TestDologin.cookieStore);

        //調取真實的接口只能設置header,傳值token
//        httpGet.setHeader("token",TestDologin.token);

        HttpResponse response = httpClient.execute(httpGet);
        /*
        下面要驗證幾種斷言方法:
        1、判斷接口返回是否是200
        2、判斷一頁返回的門店列表有20條
        3、判斷從第5位開始評分由大到小
        4、判斷與數據庫中獲取數據是否一致
         */

        //1、判斷接口返回是否是200
        Assert.assertEquals(response.getStatusLine().getStatusCode(),200,"接口返回200成功");

        String result = EntityUtils.toString(response.getEntity());
        System.out.println(result);

        JSONObject resultobject = new JSONObject(result);
        JSONObject p2pdataobject = resultobject.getJSONObject("p2pdata");
        JSONArray dataArray = p2pdataobject.getJSONArray("data");

        //2、判斷一頁返回的門店列表有20條
        Assert.assertEquals(dataArray.length(),20,"一頁返回門店列表有20條");

        List scorelist = new ArrayList();
        for (int i=0;i<dataArray.length();i++){
            String dataresult = dataArray.get(i).toString();
            JSONObject dataresultobject = new JSONObject(dataresult);
            scorelist.add(dataresultobject.getInt("score"));
        }
        //3、判斷從第5位開始評分由大到小
        for (int j=4;j<scorelist.size()-1;j++){
            if ((Integer)scorelist.get(j) >= (Integer)scorelist.get(j+1)){
                System.out.println("比對成功");

            }else{
                System.out.println("比對不成功");
            }

        }

        String dataresult = dataArray.get(1).toString();
        JSONObject dataresultobject = new JSONObject(dataresult);
        MerchantId = dataresultobject.getInt("id");
        System.out.println(MerchantId);

        //4、判斷與數據庫中獲取數據是否一致
        SqlSession sqlSession = DatabaseUtil.getsqlsession();
        int marchantid = sqlSession.selectOne("getmarchantid",MerchantId);
        Assert.assertEquals(marchantid,MerchantId);

    }

}

總結:入參的處理方法有很多種:
get請求:
1、把參數拼接在url中

getMerchantListuri=getMerchantList?longitude=116.41090393066406&latitude=39.96870040893555

2、部分參數拼接在url中,其他參數和url拼接到一起

        //url要拼接參數
        URIBuilder uriBuidler = new URIBuilder(TestConfig.getCarWashDetailuri);
        //需要從其他類中依賴的參數值
        uriBuidler.setParameter("merchantId", String.valueOf(TestgetMerchantList.MerchantId));

        HttpGet httpGet = new HttpGet(uriBuidler.build());

3、定義一個List<NameValuePair>,傳入參數

        URIBuilder uriBuilder = new URIBuilder(TestConfig.getCarWashDetailuritwouri);

        //第一種寫入參數的方法,定義一個List,類型是NameValuePair
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("merchantId", String.valueOf(TestgetMerchantList.MerchantId)));
        urlParameters.add(new BasicNameValuePair("channel","6"));
        uriBuilder.setParameters(urlParameters);


        HttpGet httpGet = new HttpGet(uriBuilder.build());

post請求:
1、獲取object類型的參數
配置文件中這些寫:

doLoginParams={"phone": "15677777777","validateCode": "9612"}

寫一個工具類,定義一個獲取object參數的方法

    //定義一個靜態(tài)的方法,獲取post接口的參數值,返回類型是Object類型
    public static Object GetParams(ParamsName name){
        if (name.equals(ParamsName.DOLOGINPARAMS)){
            Object doLoginParams = resourceBundle.getObject("doLoginParams");
            return doLoginParams;
        }
        return null;
    }

在httpclient中入參直接這樣寫:

TestConfig.doLoginParams = ConfigFile.GetParams(ParamsName.DOLOGINPARAMS);

        //傳入參數

        StringEntity entity=new StringEntity(TestConfig.doLoginParams.toString(),"gbk");
        httpPost.setEntity(entity);

注:直接配置文件中取String類型的參數值也行,不用傳object的也可以

2、配置文件中寫部分參數

doLoginParamstwo={"phone": "15677777777"}

工具類中寫,定義一個返回map類型參數的方法:

    //定義一個靜態(tài)的方法,獲取post接口的參數值,返回類型為map,這樣有些參數可以依賴其他的接口,或者手動填寫參數
    public static Map<String,Object> GetParamswithMap(ParamsName name){
        if (name == ParamsName.DOLOGINPARAMSTWOPARAMS){
            String doLoginParams = resourceBundle.getString("doLoginParamstwo");
            return JSON.parseObject(doLoginParams);
        }
        return null;

    }

在httpclient中入參直接這樣寫:

TestConfig.doLoginParamstwo = ConfigFile.GetParamswithMap(ParamsName.DOLOGINPARAMSTWOPARAMS);

        //傳入參數
        Map<String,Object> map = TestConfig.doLoginParamstwo;
        map.put("validateCode",TestSendVerificationCode.validate_code);//也可以使用數據依賴傳參數


        StringEntity entity=new StringEntity(map.toString(),"gbk");
        httpPost.setEntity(entity);

十三、TestNg的擴展學習總結:
1、添加依賴:

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.9.10</version>
        </dependency>

2、寫一個方法,使用@Test注解

package com.course.LearnTestNgCases;

import org.testng.annotations.Test;

/*
test注解
 */
public class BasicAnnotation {
    @Test
    public void testcaseone(){
        System.out.println("這是最基本的注解,用來把方法標記為測試的一部分");
    }
}

3、@BeforeMethod @AfterMethod注解

package com.course.LearnTestNgCases;

import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/*
test注解
 */
public class BasicAnnotation {
    @Test
    public void testcaseone(){
        System.out.println("這是最基本的注解,用來把方法標記為測試的一部分");
    }
    @Test
    public void testcasetwo(){
        System.out.println("第二個測試方法");
    }
    @BeforeMethod
    public void beforemethod(){
        System.out.println("測試方法之前運行");
    }
    @AfterMethod
    public void aftermethod(){
        System.out.println("測試方法之后運行");
    }
}

測試結果為:


image.png

4、@BeforeClass @AfterClass 注解

package com.course.LearnTestNgCases;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;

/*
test注解
 */
public class BasicAnnotation {
    public BasicAnnotation(){
        System.out.println("kkkkkk");
    }
    @Test
    public void testcaseone(){
        System.out.println("這是最基本的注解,用來把方法標記為測試的一部分");
    }
    @Test
    public void testcasetwo(){
        System.out.println("第二個測試方法");
    }
    @BeforeMethod
    public void beforemethod(){
        System.out.println("測試方法之前運行");
    }
    @AfterMethod
    public void aftermethod(){
        System.out.println("測試方法之后運行");
    }
    @BeforeClass
    public void beforeclass(){
        System.out.println("在類之前運行");
    }
    @AfterClass
    public void afterclass(){
        System.out.println("在類之后運行");
    }
}

測試結果:


image.png

5、@BeforeTest、@AfterTest注解

package com.course.LearnTestNgCases;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;

/*
test注解
 */
public class BasicAnnotation {
    @Test
    public void testcaseone(){
        System.out.println("這是最基本的注解,用來把方法標記為測試的一部分");
    }
    @Test
    public void testcasetwo(){
        System.out.println("第二個測試方法");
    }
    @BeforeSuite
    public void beforesuite(){
        System.out.println("beforesuite測試套件,在BeforeClass類之前運行");
    }
    @AfterSuite
    public void aftersuite(){
        System.out.println("aftersuite測試套件,在Afterclass類之后運行");
    }
    @BeforeTest
    public void beforetest(){
        System.out.println("beforetest在測試之前運行");
    }
    @AfterTest
    public void aftertest(){
        System.out.println("aftertest在測試之后運行");
    }
    @BeforeClass
    public void beforeclass(){
        System.out.println("在類之前運行");
    }
    @AfterClass
    public void afterclass(){
        System.out.println("在類之后運行");
    }
    @BeforeMethod
    public void beforemethod(){
        System.out.println("測試方法之前運行");
    }
    @AfterMethod
    public void aftermethod(){
        System.out.println("測試方法之后運行");
    }

}

測試結果:


image.png

6、@BeforeSuite、@AfterSuite注解

package com.course.LearnTestNgCases;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;

/*
test注解
 */
public class BasicAnnotation {
    @Test
    public void testcaseone(){
        System.out.println("這是最基本的注解,用來把方法標記為測試的一部分");
    }
    @Test
    public void testcasetwo(){
        System.out.println("第二個測試方法");
    }
    @BeforeMethod
    public void beforemethod(){
        System.out.println("測試方法之前運行");
    }
    @AfterMethod
    public void aftermethod(){
        System.out.println("測試方法之后運行");
    }
    @BeforeClass
    public void beforeclass(){
        System.out.println("在類之前運行");
    }
    @AfterClass
    public void afterclass(){
        System.out.println("在類之后運行");
    }
    @BeforeSuite
    public void beforesuite(){
        System.out.println("beforesuite測試套件,在BeforeClass類之前運行");
    }
    @AfterSuite
    public void aftersuite(){
        System.out.println("aftersuite測試套件,在Afterclass類之后運行");
    }
}

測試結果:


image.png

總結:
testng的注解執(zhí)行順序如下:
BeforeSuite > BeforeTest > BeforeClass > BeforeMethod > Test
測試套件 > 測試組 > 測試類 > 測試方法 >具體的測試方法

7、套件測試:先創(chuàng)建3個類,其中一個是SuiteConfig類

package com.course.LearnTestNgCases.Suite;

import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;

/*
測試套件的配置
 */
public class SuiteConfig {

    @BeforeSuite
    public void beforesuite(){
        System.out.println("在測試套件前執(zhí)行");
    }

    @AfterSuite
    public void aftersuite(){
        System.out.println("在測試套件后執(zhí)行");
    }
}

另外兩個是LoginTest類和PayTest類

package com.course.LearnTestNgCases.Suite;

import org.testng.annotations.Test;

/*
測試類
 */
public class LoginTest {

    @Test
    public void logintest(){
        System.out.println("登錄成功");
    }
}

package com.course.LearnTestNgCases.Suite;

import org.testng.annotations.Test;

public class PayTest {
    @Test
    public void paytest(){
        System.out.println("支付成功");
    }
}

創(chuàng)建suite.xml 名稱可以隨意起,該文件中寫測試類執(zhí)行的順序:


image.png

與上面講的注解順序一致。
把suite.xml文件寫全,如下:

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="學習testng的suite測試套件">
    <test name="測試LoginTest類" >
        <classes>
            <class name="com.course.LearnTestNgCases.Suite.LoginTest">
                <include name="com.course.LearnTestNgCases.Suite.LoginTest.logintest"/>
            </class>
            <class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
        </classes>
    </test>

    <test name="測試PayTest類">
        <classes>
            <class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
            <class name="com.course.LearnTestNgCases.Suite.PayTest"/>
        </classes>
    </test>
</suite>
image.png

右鍵運行suite.xml文件,查看運行結果:


image.png

正常情況下,這些寫會比較合理:

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="學習testng的suite測試套件">
    <test name="測試LoginTest類" >
        <classes>
            <class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
            <class name="com.course.LearnTestNgCases.Suite.LoginTest"/>
            <class name="com.course.LearnTestNgCases.Suite.PayTest"/>
        </classes>
    </test>
</suite>

運行結果:


image.png

8、忽略測試
@Test(enabled = false) 忽略測試
@Test(enabled = true) 執(zhí)行測試

package com.course.LearnTestNgCases;

import org.testng.annotations.Test;

public class IgnoreTest {
    @Test
    public void ignore1(){
        System.out.println("執(zhí)行測試");
    }
    @Test(enabled = false)
    public void ignore2(){
        System.out.println("enable=false 忽略測試");
    }
    @Test(enabled = true)
    public void ignore3(){
        System.out.println("enable=true 執(zhí)行測試");
    }
}

執(zhí)行結果:


image.png

9、testng組測試
9.1、方法分組
在方法上加@Test(groups="groupsname")進行分組
@BeforeGroups(“groupsname”) 、@AfterGroups(“groupsname”)注解,在對應的測試組執(zhí)行之前執(zhí)行,在對應的測試組執(zhí)行之后執(zhí)行

package com.course.LearnTestNgCases.Groups;

import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;

public class TestGroups {

    @Test(groups = "server")
    public void testserver(){
        System.out.println("服務端測試用例");
    }
    @Test(groups = "client")
    public void testclient(){
        System.out.println("客戶端測試用例");
    }

    @BeforeGroups("server")
    public void beforeservergroups(){
        System.out.println("在服務端測試用例執(zhí)行之前執(zhí)行");
    }
    @AfterGroups("server")
    public void afterservergroups(){
        System.out.println("在服務端測試用例執(zhí)行之后執(zhí)行");
    }

    @BeforeGroups("client")
    public void beforeclientgroups(){
        System.out.println("在客戶端測試用例執(zhí)行之前執(zhí)行");
    }
    @AfterGroups("client")
    public void afterclientgroups(){
        System.out.println("在客戶端測試用例執(zhí)行之后執(zhí)行");
    }

}

groups組的名字要一致。

執(zhí)行結果:


image.png

9.2 類分組
先建3個類,在每個類上加不同的groups,在testng.xml中通過配置groups來分組執(zhí)行測試用例

image.png

groupclass1類,@Test(groups = "student"):

package com.course.LearnTestNgCases.Groups;

import org.testng.annotations.Test;

@Test(groups = "student")
public class groupclass1 {

    public void testgroupclass1(){
        System.out.println("這是student分組下的測試用例1");
    }
}

groupclass2類,@Test(groups = "student")

package com.course.LearnTestNgCases.Groups;

import org.testng.annotations.Test;

@Test(groups = "student")
public class groupclass2 {
    public void testgroupclass2(){
        System.out.println("這是student分組下的測試用例2");
    }
}

groupclass3類,@Test(groups = "teacher"),和上面兩個類的分組不同

package com.course.LearnTestNgCases.Groups;

import org.testng.annotations.Test;

@Test(groups = "teacher")
public class groupclass3 {

    public void testgroupclass3(){
        System.out.println("這是teacher分組下的測試用例");
    }
}

新建一個xml,添加groups配置,只包含student組


image.png

右鍵執(zhí)行該xml,查看結果中只執(zhí)行了groupclass1和groupclass2兩個類,groupclass3類沒有執(zhí)行。


image.png

加上teacher分組,再次執(zhí)行就能看到執(zhí)行了groupclass3類


image.png
image.png

10、異常測試:
執(zhí)行中有異常,期望測試結果就是異常,用@Test(expectedExceptions = RuntimeException.class)注解使測試用例執(zhí)行結果為正常

package com.course.LearnTestNgCases;

import org.testng.annotations.Test;

/*
異常測試,期望測試結果就是異常,用@Test(expectedExceptions = RuntimeException.class)注解使測試用例執(zhí)行結果為正常
 */
public class Exceptiontest {

    @Test(expectedExceptions = RuntimeException.class)
    public void testExceptiontest(){
        System.out.println("執(zhí)行時會有異常,預期結果就是希望有異常,所以測試用例會執(zhí)行通過。本用例要拋出一個異常");


        //主動拋出一個異常或者制造一個異常
//        throw new RuntimeException();
        int a=10;
        int b=0;
        int c=a/b;//b為0,肯定會報異常。

    }
}

11、依賴測試
11.1 依賴方法
dependsOnMethods = {"testfirst"}
代碼如下:

    @Test
    public void testfirst(){
        System.out.println("testfirst方法執(zhí)行通過后再執(zhí)行下面的方法,執(zhí)行失敗不執(zhí)行下面的方法");
    }

    @Test(dependsOnMethods = {"testfirst"})
    public void testsencond(){
        System.out.println("testsencond方法依賴testfirst方法");

    }

11.2 依賴組
dependsOnGroups = "testthird"
代碼如下:


    @Test(groups = "testthird")
    public void testthird(){
        System.out.println("testthird方法執(zhí)行通過后再執(zhí)行下面依賴該組的方法,執(zhí)行失敗不執(zhí)行下面的方法");
    }

    @Test(dependsOnGroups = "testthird")
    public void testfourth(){
        System.out.println("testfourth方法依賴testthird方法");
    }

12、參數化測試
12.1 參數化測試,xml文件參數化
在xml中寫入:


image.png

在類中寫入@parameter({"name","age"})注解,輸入參數


image.png

12.2 參數化測試,@DataProvider方法
先寫一個dataprovider方法,定義一個name值,返回類型必須是Object[][]或者Iterator<Object[]>,參數個數可以為一個或者多個

    //數據方法
    @DataProvider(name = "data")
    public Object[][] dataprovider(){
        Object[][] objects = new Object[][]{
                {"zhaowei",40,"kkk",180},
                {"suyoupeng",42,"jjj",170},
                {"linxinru",43,"ppp",160}
        };
        return objects;
    }

測試用例方法,@Test注解中要寫(dataProvider = "data"),data名稱與上面定義的name值一致,參數值傳了3遍,執(zhí)行結果有三條記錄:

    //測試用例
    @Test(dataProvider = "data")
    public void testdataprovider(String name,int age,String address,int height){
        System.out.println("通過DataProvider傳過來的name是"+name+",age是"+age+",地址是"+address+",身高是"+height);
    }
image.png

定義兩個方法,分別傳入不同的值,通過dataprovider傳值
方法代碼如下:

    @Test(dataProvider = "datatwo")
    public void testdologin(String username,String password){
        System.out.println("登錄成功,用戶名是"+username+",密碼是"+password);
    }
    @Test(dataProvider = "datatwo")
    public void testgetmerchantlist(int merchantid,String merchantname,String merchantaddress){
        System.out.println("查詢商戶列表成功,商戶id是"+merchantid+",商戶名稱是"+merchantname+",商戶地址是"+merchantaddress);
    }

dataprovider方法為:

    //數據方法二,根據不同的方法傳不同的值進來
    @DataProvider(name = "datatwo")
    public Object[][] dataprovidertwo(Method method){
        Object[][] result = null;
        if (method.getName().equals("testdologin")){
             result = new Object[][]{
                    {"18600228000","123456"}
            };
        }else if (method.getName().equals("testgetmerchantlist")){
            result = new Object[][]{
                    {38159,"秦小姐洗車門店","北京市通州區(qū)"}
            };
        }
        return result;

    }

根據調Method類的getName方法獲取不同方法的名稱,判斷不同的名稱返回不同的值。


image.png

擴展@dataprovider的使用:
12.3 從另一個類中獲取數據依賴
第一個類中專門寫數據

package com.course.LearnTestNgCases.GroupsAndDataprovider;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/*
專門提供數據的類,在使用數據信息的地方用@Test(dataProvider = "data",dataProviderClass = class111.class)來引入
 */

public class class111 {

    @DataProvider(name = "data")
    public static Object[][] dataprovider(){
        Object[][] objects = new Object[][]{
                {"zhaowei",40,"kkk",180},
                {"suyoupeng",42,"jjj",170},
                {"linxinru",43,"ppp",160}
        };
        return objects;
    }

}

第二個類用@Test(dataProvider = "data",dataProviderClass = class111.class)來引入測試數據

package com.course.LearnTestNgCases.GroupsAndDataprovider;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;


public class class222 {

    //測試用例,從class111類中引入測試數據
    @Test(dataProvider = "data",dataProviderClass = class111.class)
    public void testdataprovider(String name,int age,String address,int height){
        System.out.println("通過DataProvider傳過來的name是"+name+",age是"+age+",地址是"+address+",身高是"+height);
    }
}

12.4 返回類型為迭代器Iterator<Object[]>
在class111類中寫入如下代碼:

    @DataProvider(name="getmerchatidAndname")
    public static Iterator<Object[]> getmerchatidAndnameDataProvider() throws IOException {
        //定義Object[]類型的list
        List<Object[]> result=new ArrayList<Object[]>();
        //創(chuàng)建sqlsession的實例
        SqlSession session= DatabaseUtil.getsqlsession();
        //從數據庫中獲取數據信息
        List<Object> alldata=session.selectList("getmerchatidAndname");


       //從數據庫中獲取的數據類型是Object,該方法返回的類型是Object[],所以需要做類型轉換
        //用foreach循環(huán)對alldata中的每一條數據做形式轉換,并存儲到result中
        for (Object u : alldata) {
            //做一個形式轉換
            result.add(new Object[] { u });
        }
        //然后result的迭代器類型的數據
        return result.iterator();


        //下面的代碼與上面的代碼功能類似就是做一個形式轉換,從Object類型轉換成Object[]類型
        //生成一個迭代器,對迭代器中的每一個數據做形式轉換
        //迭代器的方法有hasNext()是否有值,有值為真,沒值為假,it.next()獲取具體的值
//        Iterator it=alldata.iterator();
//        while(it.hasNext()){
//            result.add(new Object[] { it.next() });
//        }
//        return  result.iterator();

    }

然后在model層建一個實體類:

package com.course.model;

public class Cfwshop {
    private int id;
    private String shop_name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getShop_name() {
        return shop_name;
    }

    public void setShop_name(String shop_name) {
        this.shop_name = shop_name;
    }

    @Override
    public String toString() {
        return "Cfwshop{" +
                "id=" + id +
                ", shop_name='" + shop_name + '\'' +
                '}';
    }
}

再在SQLMapper.xml中寫sql語句,id與session.selectList("getmerchatidAndname");中的值一致。

    <select id="getmerchatidAndname" resultType="com.course.model.Cfwshop">
        select id,shop_name from autosvc.cfw_shop LIMIT 10;
    </select>

再在測試類中調dataprovider數據類,入參類型是Cfwshop cfwshop 實體類,通過get方法獲取具體值。

    //測試用例,從class111類中引入Iterator類型的測試數據
    //需要創(chuàng)建一個Cfwshop的實體類,這樣才能接收到具體的值
    @Test(dataProvider = "getmerchatidAndname",dataProviderClass = class111.class)
    public void testgetmerchatidAndname(Cfwshop cfwshop){
        System.out.println("商戶id為:"+cfwshop.getId()+",商戶名稱為:"+cfwshop.getShop_name());

    }

13、多線程測試

@Test(invocationCount = 10,threadPoolSize = 3)

package com.course.LearnTestNgCases.MultiThread;

import org.testng.annotations.Test;

/*
驗證使用多線程
 */
public class MultiThreadOnAnnotation {

    //設置10個線程,3個線程池,從線程池中取3個線程,用3個線程執(zhí)行10遍測試用例,如果不設置threadPoolSize,就會默認用一個線程執(zhí)行10遍,所以要設置一個線程池
    @Test(invocationCount = 10,threadPoolSize = 3)
    public void testMultiThreadOnAnnotation(){
        System.out.println("1");
        System.out.printf("Thread id: %s%n",Thread.currentThread().getId());
    }
}

image.png

在xml中通過配置線程數來控制線程:
先寫一個測試類,有3個測試方法:

package com.course.LearnTestNgCases.MultiThread;

import org.testng.annotations.Test;

public class MultiThreadOnXml {
    @Test
    public void test1(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }

    @Test
    public void test2(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }

    @Test
    public void test3(){
        System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
    }
}

查看xml文件:
parallel="methods":

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="methods" thread-count="3">
    <!--
    parallel="methods",methods級別,所有用例都可以在不同的線程中執(zhí)行
     thread-count:代表了最大并發(fā)線程數
      xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
    -->
    <test name="testthread">
        <classes>
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
        </classes>
    </test>
</suite>

執(zhí)行結果為:


image.png

parallel="classes":

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="classes" thread-count="3">
    <!--
    parallel="methods",methods級別,所有用例都可以在不同的線程中執(zhí)行
     thread-count:代表了最大并發(fā)線程數
      xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
      
    parallel="classes",classs級別:
           相同的class tag 下的用例在同一個線程中執(zhí)行
           不同的class tag 下的用例可以在不同的線程中執(zhí)行
    -->
    <test name="testthread">
        <classes>
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
        </classes>
    </test>
</suite>

測試結果:


image.png

創(chuàng)建不同的classs:

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="classes" thread-count="6" >
    <!--
    parallel="methods",methods級別,所有用例都可以在不同的線程中執(zhí)行
     thread-count:代表了最大并發(fā)線程數
      xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池

    parallel="classes",classs級別:
           相同的class tag 下的用例在同一個線程中執(zhí)行
           不同的class tag 下的用例可以在不同的線程中執(zhí)行

    -->
    <test name="testthread">
        <classes name="demo1">
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
        </classes>
        <classes name="demo2">
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
        </classes>

    </test>
</suite>

測試結果,線程會有些亂:


image.png
parallel="tests",tests級別:
       相同的test tag 下的用例在同一個線程中執(zhí)行
       不同的test tag 下的用例可以在不同的線程中執(zhí)行
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="tests" thread-count="3" >
    <!--
    parallel="methods",methods級別,所有用例都可以在不同的線程中執(zhí)行
     thread-count:代表了最大并發(fā)線程數
      xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池

    parallel="classes",classs級別:
           相同的class tag 下的用例在同一個線程中執(zhí)行
           不同的class tag 下的用例可以在不同的線程中執(zhí)行

    parallel="tests",tests級別:
           相同的test tag 下的用例在同一個線程中執(zhí)行
           不同的test tag 下的用例可以在不同的線程中執(zhí)行
    -->
    <test name="testthread">
        <classes name="demo1">
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
        </classes>

    </test>
</suite>
image.png

設置不同的tests:

<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="tests" thread-count="3" >
    <!--
    parallel="methods",methods級別,所有用例都可以在不同的線程中執(zhí)行
     thread-count:代表了最大并發(fā)線程數
      xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池

    parallel="classes",classs級別:
           相同的class tag 下的用例在同一個線程中執(zhí)行
           不同的class tag 下的用例可以在不同的線程中執(zhí)行

    parallel="tests",tests級別:
           相同的test tag 下的用例在同一個線程中執(zhí)行
           不同的test tag 下的用例可以在不同的線程中執(zhí)行
    -->
    <test name="testthread">
        <classes name="demo1">
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
        </classes>

    </test>
    <test name="testthread2">
        <classes name="demo2">
            <class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
        </classes>

    </test>
</suite>

測試結果:


image.png

總結:正常情況下,用methods級別會比較多,多條用例在多個線程下執(zhí)行,會節(jié)省測試時間。

14、超時測試
@Test(timeOut = 3000)//超時等待3000毫秒,就是3秒

package com.course.LearnTestNgCases;

import org.testng.annotations.Test;

/*
超時測試,測試中調接口可能會超時,導致調接口失敗,通過設置超時時間,使用例執(zhí)行通過。
 */
public class TimeOutTest {

    @Test(timeOut = 3000)//超時等待3000毫秒,就是3秒
    public void test1() throws InterruptedException {
        //設置一個線程,讓線程休眠2000毫秒,等待時間大于休眠時間,所以用例會執(zhí)行成功
        Thread.sleep(2000);
    }

    @Test(timeOut = 3000)
    public void test2() throws InterruptedException {
        //設置一個線程,讓線程休眠4000毫秒,等待時間小于休眠時間,所以用例會執(zhí)行失敗
        Thread.sleep(4000);
    }

}

注:更多的超時設置可以參考
摘抄自:https://blog.csdn.net/u011191463/article/details/78664896

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 感謝原作者的奉獻,原作者博客地址:http://blog.csdn.net/zhu_ai_xin_520/arti...
    狼孩閱讀 14,299評論 1 35
  • TestNG默認情況下,會生產兩種類型的測試報告HTML的和XML的。 測試報告位于 "test-output" ...
    我為峰2014閱讀 8,020評論 0 5
  • 當從微博這一八卦發(fā)動機上獲悉了某個消息之后,就可以預料到又要有無數的公眾號和抓熱點的人要趕緊下手,搶占高地了。 果...
    梅疏雨閱讀 370評論 0 0
  • 朋友最近受腰痛困擾,多次求醫(yī)無果,連吃飯都不能久立,突然覺得我們是不是就這樣老了?可是,我們怎么可以老?我們還在年...
    一百年不孤獨閱讀 199評論 0 0

友情鏈接更多精彩內容