基於TestNG+Rest Assured+Allure的接口自動化測試框架

一、前言

當今,“自動化測試”大行其道,其中“接口自動化測試”便是同行們談得最多的話題之一。瞭解測試金字塔分層理念的童鞋都清楚,接口自動化測試有以下優點。

  • 投入低,產出高。
  • 比UI自動化更穩定。
  • 比單元測試更接近真實業務。

正因爲以上優點,接口自動化測試逐漸成爲了業界主流,各種工具/框架層出不窮,比如Postman,Jmeter,Htttpclient,Rest assured,HttpRunnerManager等。

二、背景

此前筆者曾基於Jenkins+Ant+Git+Jmeter搭建過一套接口自動化框架,期間亦針對Jmeter做了許多功能的擴展,比如:生成excle結果文件、數據庫斷言、自動提交缺陷、自動更新案例執行結果至Testlink等。雖說Jmeter簡單易上手,但在筆者看來,其並不是接口自動化測試的首選,其中的原因暫不祥談,畢竟仁者見仁。
近段時間,筆者一直在思索,學習前輩們優秀的經驗,並從公司項目架構出發,搭建了一套基於Jenkins+Maven+Git+TestNG+RestAssured+Allure的持續集成測試框架,相比原先Jmeter的那套,其易用性更好、效率更高、擴展性更強。

三、框架理念

  • 根據用例模板編寫測試用例(包含響應報文斷言及接口入參等)。
  • 編寫數據庫斷言文件。
  • 編寫測試類。
  • 配置環境及測試用例集。
  • Jenkins選擇運行環境及用例集,觸發構建。
  • 生成測試報告,郵件通知相關人員。

四、操作步驟

1、編寫用例

用例文件名稱與測試類名一致,比如開戶的測試類名爲OpenAcc,則用例文件名爲OpenAcc.xls,用例模板由以下幾部分組成。

  • caseNo、testPoint分別與案例管理系統的案例編號、案例標題對應,方便後續同步執行結果。
  • preResult爲響應報文斷言,目前支持jsonpath、包含斷言、不包含斷言。
  • YorN爲案例執行標誌,Y表示執行。
  • tableCheck爲數據庫斷言標誌,Y表示進行數據庫斷言。
  • 其他字段爲接口入參,目前支持以下五種供數方式。
    (1)自定義函數供數。引用格式爲:__phone()表示生成11位手機號。__idno()表示生成18位身份證號。
    (2)查詢數據池供數。引用格式爲:${dp.sql(select accountNo from M_account where status = 1)}
    (3)查詢數據庫供數。引用格式爲:${db.sql(select accountNo from M_account_card where status = 1)}
    (4)先接口請求,然後提取響應報文供數。引用格式爲:${SendmsgYg.case023.post($.data.code)},表示先以post方式發送SendmsgYg接口請求,然後再提取響應報文的code字段。支持接口之間的多重依賴。
    (5)先接口請求,然後查詢數據庫/池供數。引用格式爲:${SendmsgYg.case023.post.db.sql(select accountNo from M_account_card where status = 1)},表示先以post方式發送SendmsgYg接口請求,然後再查詢數據庫(db)/數據池(dp)獲取數據。
2、編寫數據庫斷言

數據庫斷言文件名稱與測試類名一致,比如開戶的測試類名爲OpenAcc,則斷言文件爲OpenAcc.xml。一個接口對應一個數據庫斷言文件,一個斷言文件裏可包含多條案例,每條案例可以斷言多張表,模板如下。
ps:爲了提升效率,後續亦會提供一個生成數據庫斷言文件小工具。

<?xml version="1.0" encoding="utf-8"?>

<dbCheck dbCheck_name="開戶綁卡數據庫檢查點">
    <caseNo case_no="case085"> <!--案例編號-->
        <table table_name="M_ACCOUNT"> <!--表名-->
            <priKey key_name="ACCOUNT_NO">ACCOUNT_NO</priKey>      <!--主鍵-->
            <column column_name="CUST_ID">CUST_ID</column>         <!--其他字段的預期結果-->
            <column column_name="MERCHANT_ID">MERCHANT_ID</column> <!--其他字段的預期結果-->
            <column column_name="ACCOUNT_STATUS">1</column>        <!--其他字段的預期結果-->
            <column column_name="ORGAN_NO">0019901</column>        <!--其他字段的預期結果-->
        </table>
    </caseNo>

    <caseNo case_no="case086">
        <table table_name="M_ACCOUNT_CARD">
            <priKey key_name="ACCOUNT_NO">ACCOUNT_NO</priKey>
            <priKey key_name="CARD_NO">CARD_NO</priKey>
            <column column_name="CARD_TYPE">2</column>
            <column column_name="MERCHANT_ID">MERCHANT_ID</column>
            <column column_name="CUST_ID">CUST_ID</column>
            <column column_name="CARD_IMG">CARD_IMG</column>
            <column column_name="OPEN_BANKNAME">NOTNULL</column>
        </table>
    </caseNo>
</dbCheck>

對於未確定的預期結果,使用變量代替,後續編寫測試類時再做映射。

3、編寫測試類

測試類分爲兩大類,一是隻需響應報文斷言,二是需要響應報文及數據庫斷言。測試人員按照以下模板編寫腳本即可。

/*
 *短信發送接口
 * 環境參數在SetUpTearDown 父類定義
 */
@Feature("分類賬戶改造")
public class SendmsgYg extends SetUpTearDown {

    @Story("發送短信")
    @Test(dataProvider = "dataprovider",
            dataProviderClass = DataProviders.class,
            description = "發送短信")
    public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {

        //發送請求
        Response response = RunCaseJson.runCase(bodyString, "post");

        //只進行響應報文斷言
        asserts(caseMess, bodyString, response.asString(),"",null);
    }
}

數據庫斷言文件中的變量,可通過調用封裝的方法取值,比如查數據庫、提取響應報文、調用接口等方式。

/*
 *開立分類賬戶
 * 環境參數在SetUpTearDown 父類定義
 */
@Feature("分類賬戶改造")
public class OpenYg extends SetUpTearDown {

    @Story("分類賬戶開戶")
    @Test(dataProvider = "dataprovider",
            dataProviderClass = DataProviders.class,
            description = "開戶")
    public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {

        //發送請求
        Response response = RunCaseJson.runCase(bodyString, "post");

        //如果需要數據庫斷言,此處添加斷言文件變量的map映射
        //可通過調用封裝的方法取值,比如查數據庫、提取響應報文、調用接口等方式。
        Map<String, String> map = new HashMap<>();
        //查詢數據庫獲取,取不到值返回""
        String account = DataBaseCRUD.selectData("select accountNo from M_ACCOUNT where status =1");
        //提取響應報文,取不到值返回""
        String custId = RespondAssertForJson.getBuildValue(response.asString(),"$.data.custid");
        //執行SendmsgYg接口的case023案例,然後提取響應報文的merchanId ,取不到值返回""
        String merchanId = RespondAssertForJson.getBuildValue("","${SendmsgYg.case023.post($.data.merchanId)}");

        map.put("ACCOUNT_NO",account);
        map.put("CUST_ID",custId);
        map.put("MERCHANT_ID",merchanId);

        //斷言(包含響應報文斷言和數據庫斷言)
        String xmlFileName = this.getClass().getSimpleName(); //數據庫斷言xml文件名(與類名保持一致)
        asserts(caseMess, bodyString, response.asString(),xmlFileName,map);
    }
}
4、用例集

對於多個suite,可通過suite-files配置。testng.xml文件配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="IIACCOUNT自動化測試" parallel="classes">

    <listeners>
        <!--失敗重跑-->
        <listener class-name="com.iiaccount.listener.FailedRetryListener"/>
    </listeners>

    <test verbose="2" name="IIACCOUNT_YG">

        <classes>
            <class name="com.iiaccout.yiguan.OpenYg"/>
            <class name="com.iiaccout.yiguan.SendmsgYg"/>
        </classes>
    </test>
</suite>
5、Jenkins構建

選擇環境及測試用例集,開始構建,構建完成後生成測試報告及日誌。也可根據需要設置定時構建,持續進行質量監控,具體的設置方法在筆者的另一篇文章《基於Jmeter的性能測試框架搭建》改進一有提到。



6、報告分析

在這個注重顏值的世界,allure框架出來的測試報告絕對稱得上“報告界的小鮮肉”,筆者在文章《高大上的測試報告-Allure開源框架探索》亦有詳細介紹。
測試報告總覽包含用例通過率、測試套件、環境、feature、類別、趨勢等信息。以下示例截圖的案例全部執行失敗,所以總覽的通過率是0%。


類別主要展現失敗的用例信息,可根據項目情況自定製報告內容,比如請求報文、響應報文、斷言結果等。


時間刻度展現了每條案例的執行時間。

報告更詳細的功能可閱覽《高大上的測試報告-Allure開源框架探索》一文。

五、框架實現方案

1、工具/框架
  • Jenkins
  • Maven
  • Gitlab
  • TestNG
  • Rest Assured
  • Allure
2、工程目錄


3、pom依賴

支持多環境(sit,uat)切換,結合Jenkins使用。

<?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>HFIIACCOUNT</groupId>
    <artifactId>ApiAutoTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--通過“-D”引用變量-->
    <properties>
        <aspectj.version>1.8.10</aspectj.version>
        <!-- 解決mvn編譯亂碼問題-->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!--外部傳參-->
        <xmlFileName></xmlFileName>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.11</version>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>ru.yandex.qatools.allure</groupId>
            <artifactId>allure-testng-adaptor</artifactId>
            <version>1.3.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.testng</groupId>
                    <artifactId>testng</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-testng</artifactId>
            <version>2.0-BETA14</version>
        </dependency>

        <dependency>
            <groupId>net.sourceforge.jexcelapi</groupId>
            <artifactId>jxl</artifactId>
            <version>2.6.12</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.13</version>
        </dependency>

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>10.2.0.4.0</version>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>

        <filters>
            <filter>src/main/filters/filter_${env}.properties</filter>
        </filters>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                    <!--生成allure-result的目錄-->
                    <systemProperties>
                        <property>
                            <name>allure.results.directory</name>
                            <value>./target/allure-results</value>
                        </property>
                    </systemProperties>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19</version>
                <configuration>
                    <suiteXmlFiles>
                        <!--該文件位於工程根目錄時,直接填寫名字,其它位置要加上路徑-->
                        <!--suiteXmlFile>src/main/resources/testngXml/${xmlFileName}</suiteXmlFile-->
                        <suiteXmlFile>${project.basedir}/target/classes/testngXml/${xmlFileName}</suiteXmlFile>
                    </suiteXmlFiles>

                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>

            <!--增加此配置,防止編譯後xls文件亂碼-->
            <!--Maven resources 插件會對文本資源文件進行轉碼,但是它無法區分文件是否是純文本文件還是二進制文件.於是二進制文件在部署過程中也就被轉碼了.-->
            <!--https://blog.csdn.net/xdxieshaa/article/details/54906476-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <nonFilteredFileExtensions>
                        <!-- 不對xls進行轉碼 -->
                        <nonFilteredFileExtension>xls</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>

        </plugins>
    </build>

    <!--通過“-P”引用變量-->
    <profiles>
        <!-- uat測試環境 -->
        <profile>
            <id>uat</id>
            <properties>
                <env>uat</env>
            </properties>

        </profile>

        <!-- sit測試環境 -->
        <profile>
            <id>sit</id>
            <properties>
                <env>sit</env>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault><!--默認啓用的是sit環境配置-->
            </activation>
        </profile>

    </profiles>

</project>
4、實現思路
  • 將每個接口的案例讀取到Map保存,其中key值爲每條案例的caseNo+testPoint+preResult+YorN+tableCheck拼裝(也是一個map),value值爲剩餘字段(接口入參)的拼裝,即接口請求的bodyString。


public static void getMap(Sheet sheet, int cols, int row, String pubArgs){

        for (int col = 0; col < cols; col++) {

            String cellKey = sheet.getCell(col, 0).getContents();//表頭
            String cellValue = sheet.getCell(col, row).getContents();//值
            if (col >= 5) {
                //appid,api,version屬於公共入參,公共入參字段在PublicArgs.properties文件進行配置
                // getBuildValue(value1,value2)方法用於轉換${}或者函數爲對應的值
                if (pubArgs.toLowerCase().contains(cellKey.toLowerCase().trim())) {
                    bodyMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
                } else {
                    dataMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
                }
            } else {
                caseMessMap.put(cellKey, cellValue);
            }
        }
        bodyMap.put("data", dataMap);
        map.put(new Gson().toJson(caseMessMap), new Gson().toJson(bodyMap));
    }
  • 對於案例中的自定義函數或供數變量,封裝了方法RespondAssertForJson.getBuildValue(var1,var2)進行轉換,目前暫支持前文提到的五種供數方式,後續可根據需要進行擴展。
    /**
     * 支持json串轉換
     * 支持自定義函數的轉換
     * 支持${}變量轉換
     *
     * @param sourchJson
     * @param key
     * @return
     */
    public static String getBuildValue(String sourchJson, String key) {
        key = key.trim();
        Matcher funMatch = funPattern.matcher(key);
        Matcher replacePattern = replaceParamPattern.matcher(key);

        log.info("key is:" + key);
        try{
            if (key.startsWith("$.")) {// jsonpath
                key = JSONPath.read(sourchJson, key).toString();  //jsonpath讀取對應的值
                log.info("key start with $.,value is:" + key);
            } else if (funMatch.find()) {//函數

                String args = funMatch.group(2);  //函數入參
                log.info("key is a function,args is:" + args);
                String[] argArr = args.split(",");
                for (int index = 0; index < argArr.length; index++) {
                    String arg = argArr[index];
                    if (arg.startsWith("$.")) {  //函數入參亦支持json格式
                        argArr[index] = JSONPath.read(sourchJson, arg).toString();
                    }
                }
                log.info("argArr:"+argArr.length);
                String value = FunctionUtil.getValue(funMatch.group(1), argArr);  //函數名不區分大小寫,返回函數值
                log.info("函數名 funMatch.group(1):" + funMatch.group(1));
                key = StringUtil.replaceFirst(key, funMatch.group(), value);  //把函數替換爲生成的值
                log.info("函數 funMatch.group():" + funMatch.group());
                log.info("key is a function,value is:" + key);
            } else if (replacePattern.find()) {//${}變量
                log.info("${}變量體:"+replacePattern.group(1));
                String var = replacePattern.group(1).trim();

                String value1 = DataBuilders.dataprovide(var);
                key = StringUtil.replaceFirst(key, replacePattern.group(), value1);  //把變量替換爲生成的值
                log.info("key is a ${} pattern,value is:" + key);
            }
            return key;

        }catch(Exception e){

            log.info(e.getMessage());
            return  null;
        }
    }
  • 通過TestNG的@DataProvider獲取Map的案例信息,進行接口請求。約定測試案例名稱爲:測試類名.xls
    /*
     *map包含兩部分json,key爲caseNo等信息,value爲接口入參
     */
    @DataProvider(name = "dataprovider")
    public static Object[][] dataP(Method method) throws IOException, BiffException, URISyntaxException {
        String className = method.getDeclaringClass().getSimpleName(); //獲取類名
        String caseFileName = className+".xls"; //測試案例名稱爲:類名.xls

        Object[][] objects = null;
        Map<String,String> map = new HashMap<String, String>();
        map = AssembledMessForJson.assembleMess(caseFileName,""); //""表示讀取所有的爲Y的case
        objects = new Object[map.size()][2];
        int i=0;
        for(Map.Entry<String, String> entry : map.entrySet()){
            objects[i][0] = entry.getKey();
            objects[i][1] = entry.getValue();
            i++;
        }
        map.clear();  //需清空map,否則案例會不斷疊加 2018-10-19 add by lrb
        return objects;
    }
  • 所有的測試類都繼承SetUpTearDown父類(詳見前文例子),父類中使用@BeforeSuite,@BeforeClass,@AfterSuite等註解來進行參數準備或數據清理。
    父類部分方法:
//環境配置
    @BeforeClass
    public void envSetUp() {
        try {
            String system = "env.properties";    //環境由filter配置
            RestAssured.baseURI = new GetFileMess().getValue("baseURI", system);
            RestAssured.basePath = new GetFileMess().getValue("basePath", system);
            RestAssured.port = Integer.parseInt(new GetFileMess().getValue("port", system));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 對每條案例進行斷言,目前暫支持響應報文斷言和數據庫斷言,後續可根據需要擴展。
    響應報文斷言:將案例文件的preResult字段(預期結果)與接口響應報文進行比對,預期結果暫支持jsonpath,包含,不包含斷言,引用格式爲:$.status=200;__contain(lrr);__notcontain(tomandy),$.cdoe=1000,多個斷言使用英文分號隔開。
    數據庫斷言:將測試類名.xml斷言文件中的預期結果與數據庫實際結果進行比對,各個字段的比對結果在測試報告中展現。
  • 對於拋異常的案例,失敗重試。
/*
 *實現IAnnotationTransformer接口,修改@Test的retryAnalyzer屬性
 */
public class FailedRetryListener implements IAnnotationTransformer {
    public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
        {
            IRetryAnalyzer retry = iTestAnnotation.getRetryAnalyzer();
            if (retry == null) {
                iTestAnnotation.setRetryAnalyzer(FailedRetry.class);
            }
        }
    }
}
  • 測試報告展現請求報文,響應報文,斷言結果等信息。
/*
 *測試報告展現
 */
public class TestStep {

    public static void requestAndRespondBody(String URL, String Body,String Respond){
        requestBody(URL,Body);
        respondBody(Respond);
    }

    @Attachment("請求報文")
    public static String requestBody(String URL, String body) {

        //格式化json串
        boolean prettyFormat = true; //格式化輸出
        JSONObject jsonObject = JSONObject.parseObject(body);
        String str = JSONObject.toJSONString(jsonObject,prettyFormat);

        //報告展現請求報文
        return URL+"\n"+str;
    }

    @Attachment("響應報文")
    public static String respondBody(String respond) {
        //報告展現響應報文
        return respond;
    }

    @Attachment("數據庫斷言結果")
    public static StringBuffer databaseAssertResult(StringBuffer assertResult){
        //報告展現數據庫斷言結果
        return assertResult;
    }

    @Attachment("響應報文斷言結果")
    public static StringBuffer assertRespond(StringBuffer assertResult){
        //報告展現數據庫斷言結果
        return assertResult;
    }
}
  • 日誌採集,log4j.properties配置如下。
log4j.rootLogger=INFO, stdout, D , E

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %C.%M(%L) ] - [ %p ]  %m%n

# 文件達到指定大小的時候產生一個新的文件
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
# TODO 部署時,修改爲指定路徑
log4j.appender.D.File=logs/apiAutoTest_debug.log  
log4j.appender.D.Append = true
# 輸出DEBUG級別以上的日誌
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %C.%M(%L) ] - [ %p ]  %m%n

### 保存異常信息到單獨文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 異常日誌文件名
# TODO 部署時,修改爲指定路徑
log4j.appender.E.File = logs/apiAutoTest_error.log
log4j.appender.E.Append = true
## 只輸出ERROR級別以上的日誌!!!
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %C.%M(%L) ] - [ %p ]  %m%n
5、Jenkins配置
5.1、插件安裝

可在線安裝插件或下載到本地安裝,下載地址

  • Maven Integration
  • Allure
  • TestNG Results
  • Parameterized Trigger
  • Email Extension
5.2、Jenkins配置

新建一個maven項目。



全局工具配置(Jenkins-系統管理-全局工具配置)。




參數化構建過程配置,選擇【choice Parameter】,配置的Name需與pom.xml文件的變量名一致,字典根據源碼中實際用途定義。此處需注意,profiles定義的屬性通過“-P”引用,下文會提及。


源碼管理配置,此處根據源碼管理工具配置。


build配置,此處通過clean test -P${env} -DxmlFileName=${xmlFileName}來把參數傳給pom,-P和-D的區別可百度。



配置構建後操作,即測試報告生成路徑。



郵件配置操作自行百度。
至此,Jenkins配置工作全部搞掂,接下來構建測試即可。
6、構建測試

六、關注點

  • 整套框架使用的是ojdbc,Maven配置ojdbc依賴需做特殊處理,詳情戳此鏈接
  • 編譯後,測試案例.xls文件亂碼。原因是Maven resources 插件會對文本資源文件進行轉碼,但是它無法區分文件是純文本文件還是二進制文件,二進制文件在部署過程中也就被轉碼了。需要在pom文件增加如下配置。
            <!--增加此配置,防止編譯後xls文件亂碼-->
            <!--Maven resources 插件會對文本資源文件進行轉碼,但是它無法區分文件是否是純文本文件還是二進制文件.於是二進制文件在部署過程中也就被轉碼了.-->
            <!--https://blog.csdn.net/xdxieshaa/article/details/54906476-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <nonFilteredFileExtensions>
                        <!-- 不對xls進行轉碼 -->
                        <nonFilteredFileExtension>xls</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
  • mvn編譯後出現亂碼。需要在pom文件增加如下配置。
<properties>
        <aspectj.version>1.8.10</aspectj.version>
        <!-- 解決mvn編譯亂碼問題-->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
  • log4j.properties配置的日誌路徑爲項目路徑下,不便於長期管理,部署時應在另指定存儲路徑。
  • Jenkins控制檯輸出亂碼,解決方法如下。
    (1)、設置jenkins所在服務器環境變量,添加系統變量。
    變量名:JAVA_TOOL_OPTIONS
    變量值:-Dfile.encoding=UTF8
    (2)、修改Tomcat配置,進入apache_tomcat/conf文件夾下,編輯server.xml,在Connector port="8080"後面加入useBodyEncodingForURI="true"
    <Connector port="8080" useBodyEncodingForURI="true" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
    (3)、啓動tomcat,運行jenkins,進入系統管理→系統設置,在全局屬性處勾選Environment variables,添加編碼環境變量LANG=zh_CN.UTF-8
  • 腳本統一放到Git管理,暫定代碼分支管理規則如下。
    master作爲穩定的分支,測試人員在dev分支進行腳本編寫,執行無誤再合併master分支,然後觸發Jenkins自動構建。


    後續在dev分支進行腳本編寫,腳本調試無誤後再push到遠程的dev分支,然後合併到master分支。


    切換到master分支,然後合併dev分支內容,然後再push到遠程倉庫。

    更詳細的操作可參考idea中git分支的使用
  • 如果jenkins的服務器部署在內網,而自己公司又沒有專門的maven遠程倉庫,第一次構建可能無法鏈接外網下載依賴包導致報錯,可通過以下方式解決。
    (1)先在本機鏈接外網下載所有的依賴包,然後再拷貝本機用戶目錄的maven倉庫(C:\Users\lenovo.m2)到jenkins服務器對應的用戶目錄下。
    (2)修改maven_home conf目錄下的settings.xml文件,增加localRepository。
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  -->
<localRepository>C:/Users/wtapp01/.m2/repository</localRepository>

七、框架擴展

上述框架目前僅侷限於測試端,嚴格意義上來說並不算真正的持續集成,後續再完善以下幾點。

  • 增加缺陷確認,提交缺陷,然後同步案例管理系統。
  • 與單元測試打通。
  • 增加代碼覆蓋率檢查。
  • 搭建統一供數平臺,通過restful api訪問。

八、github

https://github.com/Tomandy08/ApiAutoTest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章