1. 程式人生 > >加速Java應用開發速度3——單元/整合測試+CI

加速Java應用開發速度3——單元/整合測試+CI

大家可能對如下情景比較熟悉:

  • 如果開發過SSH的web專案,啟動伺服器可能會比較慢,有的專案甚至需要1分多鐘,甚至更多,這個啟動時間的等待一般就浪費了;
  • 在開發專案時,有些功能比較複雜,當時覺得思路特清晰,但是過了一段時間後,自己也忘了,完善功能時頻繁出現bug,降低開發速度;
  • 在維護專案時,不知道自己修改的對還是不對,是否存在隱患;維護速度降下來了;
  • 如果開發一個很多人都使用的介面,典型的如使用者系統,要保證比如升級時向下相容;
  • 在團隊間協作時,有時候只定義好介面,對方還沒有給實現,如何進行同步開發?

如上問題,估計只要是個開發人員,都可能遇到過;如果此時有了單元/整合測試,那我們能很好的解決這些問題。(注:加下來如果沒有特殊情況,不刻意強調 單元測試/整合測試,即提到測試是指的是單元/整合測試)

我從以下幾個方面介紹測試:

1、為什麼需要測試?

2、如何進行測試?

3、測試有哪些好處?

4、一切都需要測試嗎?

1、為什麼需要測試?

測試的目的是什麼?我的理解是:

  • 縮短髮現問題到解決問題的速度;
  • 給程式一個修改後能驗證是否正確的保證;(迴歸測試)
  • 如果是開源軟體,我們可以通過單元測試瞭解其是怎麼使用的;比如我之前通過cglib的單元測試學習過cglib的使用;

所以如果你遇到如上問題,就需要寫測試。寫測試可能是為了自己(1、2);也可能是為了幫助別人(3)。

2、如何進行測試?

很多朋友不知道如何進行測試,其實測試很簡單,別把它想複雜了,按照自己的想法測試每個功能點是否正確即可。

2.1、測試流程

單元測試流程

整合測試流程

 

整合測試流程 

 

可以看出,單元測試與整合測試唯一不同點是一個呼叫依賴系統而一個不呼叫;因為單元測試是最小粒度的測試,如在Java中是測試一個類,不會測試依賴系統;而整合測試是會測試依賴系統的。

測試的步驟:

  1. 準備環境
  2. 呼叫被測系統
  3. 驗證
  4. 清理環境

環境:也叫做夾具(fixture)或者韌體,表示呼叫被測系統時需要準備/清理的資料等等;

被測系統:在Java中就是要測試的類,如UserService;

依賴系統:測試被測系統時,其依賴的部分,如UserDao;

測試用例:包含測試方法的類,裡邊有很多測試方法來測試被測系統。

接下來仔細看看各部分都做了哪些工作。

2.2、環境

 環境,也叫做夾具(fixture),表示呼叫被測系統時需要準備/清理的資料等等;保證測試時環境是乾淨的,如不被之前的資料庫資料影響;保證每次測試都是在乾淨/新鮮的環境中執行的。所謂乾淨的環境表示如當前測試不被之前測試插入/刪除/修改的資料造成影響。在junit中可以使用:

  • @Before(setUp) 安裝夾具或準備環境:在測試用例的每個測試方法之前執行;比如建立新鮮的被測系統,單元測試時安裝Mock的依賴系統;
  • @After(tearDown)解除安裝夾具或清理環境:在測試用例的每個測試方法之後執行;比如資料庫測試時回滾事務,刪除資料;關閉檔案;
  • @BeforeClass:在整個測試用例之前執行;
  • @AfterClass:在整個測試用例之後執行;

使用如上方法,而不是直接在測試方法中安裝/解除安裝;是因為不管有沒有異常,@After/@AfterClass都會執行,這樣防止出現異常可能造成環境是不新鮮的問題。

測試時一定要保證環境是乾淨/新鮮的,才能保證每次測試的結果是一樣的。

2.3、被測系統與依賴系統

被測系統:在Java中就是被測試的Java類。

依賴系統:就是被測試Java類依賴的其他類。

如果是單元測試,一般情況下,會對依賴系統進行模擬(Mock),即給它一個假的實現;典型的如測試服務層時注入一個Mock的DAO層,這樣的好處:

  • 加快測試速度;因為不會呼叫真實的被測系統,所以速度特別快;
  • 測試還沒有完成的功能;尤其在多團隊協作時,可以只在定義好介面的情況下開發系統;

如果是整合測試時,直接注入真實的依賴系統即可,好處:

  • 完成聯調;
  • 發現自己的問題;
  • 還可能發現自己使用上問題及使用的API的問題;

單元測試雖然好,但是是隔離測試,即不會呼叫被測系統來完成測試,因為不是真實的聯調,所以很可能會潛在有一些問題,因此還是需要整合測試。(所以不是很刻意分單元或整合測試,且有些系統可能只有整合測試)

但是整合測試速度是比較慢的,一般提交給CI執行,不影響當前開發進度。

2.4、驗證

驗證的目的:是保證實際結果和我們預期的結果是否一致,說白了就是是否是我們想的那樣。

一般使用斷言來驗證,如:

Assert.assertEquals(expectedResult, actualResult); //驗證預期結果和實際結果是否相等

驗證主要有兩種:

  • 結果驗證
  • 行為驗證

結果驗證:即驗證被測系統返回的結果是否正確,如:

Java程式碼  收藏程式碼
  1. @Test  
  2. public void testCount() {  
  3.     String ql = "select count(o) from User o";  
  4.     long expectedCount = repositoryHelper.count(ql) + 1;  
  5.     User user = createUser();  
  6.     repositoryHelper.getEntityManager().persist(user);  
  7.     long acutalCount = repositoryHelper.count(ql);  
  8.     Assert.assertEquals(expectedCount, acutalCount);  
  9. }  

驗證返回的資料總數 = 插入之前的總數 + 1; 即結果驗證。此處我們使用了一種叫做相對(delta)測試;即不關心資料庫裡到底多少條,只關心實際的和預期的差。

行為驗證:即驗證被測系統是否呼叫了依賴系統的某個API ,這個只有當我們使用Mock時測試時比較簡單,如當用戶註冊時:

1、加積分

2、發系統訊息

3、……

此時我們並不能通過結果驗證是否呼叫了這些方法;那麼我們可以使用Mock技術來完成驗證是否呼叫了這些API,比如使用jmock測試框架就支援行為驗證。整合測試是很難進行行為驗證的,如果測試需要預留間諜介面。

3、測試有哪些好處?

我們寫程式碼的目的是正確的完成某個功能,如何保證正確呢?測試!所以在不使用如單元測試技術時,我們也是需要測試,但是這個測試是我們人工驗證的。缺點很明顯:

  • 不是自動的,每次需要對比預期結果與實際結果,尤其資料量/邏輯複雜時更痛苦;
  • 不是迴歸的,上次測試完成後,下次還得重複自己一遍;

為了解決這個問題,我們使用如單元測試技術來解決這個問題:

  • 測試自動化;即驗證預期結果與實際結果交給計算機吧;
  • 測試迴歸性,可以重複執行測試,驗證修改後邏輯是否還是正確的;

即測試的好處,從如上已經提煉出來了:

  • 縮短髮現問題到解決問題的時間;
  • 重複使用測試,保證修改後的程式碼還是正確的;
  • 如果做開源專案,可以提供給使用人員參考如何使用;
  • 因為單元測試都非常快,所以提升了開發速度;

4、一切都需要測試嗎?

肯定不是,一切都是相對的;哪些不需要測試呢:

  • 你非常熟悉的功能;

  • 一些簡單的CRUD;

  • 你認為不需要測試的;比如你很有把握的東西,就沒有必要浪費時間測試了;

哪些需要測試呢:
  • 複雜的業務邏輯/系統核心功能,最典型的如訂單系統:一定要有足夠的單元測試保證,這是一個電商系統的核心;還有如使用者系統、積分系統等等;
  • 框架級別/工具級別/通用級別的程式碼需要測試,即提供給第三方使用的程式碼,因為這些程式碼可能被很多系統依賴,應該保證其正確性;而且還要保證以後版本升級的向下相容;
  • 你認為需要測試的,比如你沒有把握的東西,還是寫點測試來縮短如開發web專案的重啟系統的時間吧;

 測試不是不耗時間的,沒意義的測試就是浪費時間,最典型是一些書上的對一個增刪改查進行測試,實際專案沒有任何意義。所以你應該只對自己很難駕馭的覺得有必要的程式碼進行測試。不要成為一個測試狂,什麼都測試。 

一些測試可以參考我的《es——JavaEE快速開發腳手架》中的程式碼。通過測試我得到了許多好處。 

到此我們介紹完成了測試,但是如果我們使用瞭如整合測試時,測試執行起來可能比較慢,跑一遍測試可能需要5分鐘,那怎麼辦呢?

  • 每天下班前跑一遍整合測試,然後修復,下班走人;

  • CI:持續整合,交給持續整合伺服器,自動地測試完成後把測試報告以郵件的形式發到開發人員郵箱;

------------------------------------分割線----------------------------------

接下來介紹一下CI吧。

1、為什麼需要CI

2、CI如何工作的

3、travis-ci介紹

1、為什麼需要CI

正如前邊說的,我們單獨測試可能會遇到如下問題:

  • 如果寫了一個測試,就要把所有測試跑一遍看看整個系統是否是正確的,那麼每次等待時間是非常漫長的;
  • 如果團隊中的其他成員改了功能並提交了,如何快速得到該次提交對當前系統程式碼是正確還是失敗的反饋;

那怎麼辦呢?自動化地持續整合(CI)!CI的核心就是幹這件事情的。自動化持續地整合測試。

使用CI後,如果使用Maven,可以新建多個profile:

  • 本地測試時忽略一些比較慢的測試;
  • CI伺服器上執行所有測試;

2、CI如何工作的

一個典型的持續整合流程:

  1. 定期檢測版本伺服器上是否有程式碼更新;
  2. 如果發現程式碼更新,從版本伺服器下載最新的程式碼;
  3. 自動構建並自動化的測試;
  4. 不管錯誤/失敗,生成報告給開發人員;
  5. 有些CI伺服器還能產生可執行的軟體,自動化地部署到測試機器,交給測試人員測試。

如圖所示:

 
 

持續整合伺服器其實就是一個定時器,自動幫你下載最新程式碼、編譯、測試、整合及產生報告發給開發人員。

常見的CI伺服器有:

  • Apache Continuum
  • Hudson
  • CruiseControl
  • Jenkins CI
  • TeamCity 
  • Travis CI

我09年時使用過TeamCity社群版,足夠滿足常見需求;目前我使用github託管專案,使用Travis CI進行分散式的持續整合,免費,目前看來還是不錯的。

3、travis-ci介紹

我現在開發的ES-JavaEE專案開發腳手架就是使用travis ci進行持續整合;具體參考《Getting started》進行與Github整合,其支援的語言:

支援的資料庫:

  • MySQL
  • PostgreSQL
  • MongoDB
  • CouchDB
  • Redis
  • Riak
  • RabbitMQ
  • Memcached
  • Cassandra
  • Neo4J
  • ElasticSearch
  • Kestrel
  • SQLite3

如果是Java開發人員,支援的JDK包括:OpenJDK 和 OracleJDK。 如果使用的是OpenJDK,Maven中使用ascii2native外掛時,需要如下配置: 

Java程式碼  收藏程式碼
  1. <plugin>  
  2.     <groupId>org.codehaus.mojo</groupId>  
  3.     <artifactId>native2ascii-maven-plugin</artifactId>  
  4.     <version>1.0-alpha-1</version>  
  5.     <executions>  
  6.         <execution>  
  7.             <phase>generate-resources</phase>  
  8.             <goals>  
  9.                 <goal>native2ascii</goal>  
  10.             </goals>  
  11.             <configuration>  
  12.                 <encoding>UTF-8</encoding>  
  13.                 <src>src/main/messages</src>  
  14.                 <dest>target/${project.artifactId}/WEB-INF/classes</dest>  
  15.                 <includes>messages.properties</includes>  
  16.             </configuration>  
  17.         </execution>  
  18.     </executions>  
  19.     <!-- native2ascii 使用的tools.jar -->  
  20.     <dependencies>  
  21.         <dependency>  
  22.             <groupId>com.sun</groupId>  
  23.             <artifactId>tools</artifactId>  
  24.             <version>1.7.0</version>  
  25.             <scope>system</scope>  
  26.             <systemPath>${java.home}/../lib/tools.jar</systemPath>  
  27.         </dependency>  
  28.     </dependencies>  
  29. </plugin>  

如果使用mysql,埠只能是3306。

如果想開埠測試,這是不允許的。

如下是我專案中的一個配置.travis.yml,放到專案的根下即可:

-----------------------------------

language: java           語言

env:                           環境

  - DB=mysql              使用mysql

jdk:

  - openjdk                jdk使用openjdk

mysql: 

  database: es         資料庫名為es

  username: root     使用者名稱為root

  password :            密碼為空

  encoding: utf8      編碼為utf8

install:                     安裝時執行的指令碼

  - mvn install -Dmaven.test.skip=true     mvn安裝並跳過測試

before_script:        script之前執行的測試

  - cd web              

  - mvn db:create  建立資料庫的mvn命令(此處使用了 maven-db-plugin 外掛)

  - mvn db:schema  建立指令碼的mvn命令

  - mvn db:data        安裝資料的mvn命令

  - cd ..

script:                      測試時執行的腳步

  - cd common 

  - mvn test              測試common子模組

  - cd ..

  - cd web

  - mvn test -Pit       測試web子模組,並指定使用it profile測試(即整合測試的配置,具體參考pom.xml中的profile/it)

notifications:          觸發

  email:                  測試完成後測試報告發到哪

    - [email protected]  

-----------------------------------

持續整合不能修復程式碼的錯誤,而是和單元測試一樣,縮短髮現問題帶解決問題的時間,這樣可以提高開發效率,降低專案風險,提高專案的穩定性。而且尤其是團隊協作時,可以發現其他人的程式碼是否對自己的程式碼產生影響。 

到此我們利用單元測試+CI可以加速開發人員的開發速度。利用好單元測試和CI,不要純粹為了單元測試和CI而去做這些事情。

本文沒有介紹TDD,TDD並不會那麼美好,我認為我們可以借鑑TDD的一些思想,但決不能迷信TDD,有時候,尤其如開發企業應用,先寫功能再寫測試可能效率更高,而且大部分時候是不需要TDD的。而且我也沒能在實際專案中獲取太多TDD的好處,但是我獲得了測試的好處。

本文也沒有介紹測試覆蓋率,我認為不要一味的追求覆蓋率,有時候有的覆蓋率沒有任何意義。所以不要讓為了覆蓋率而覆蓋率拖慢了專案開發進度。

寫道 老闆為我的程式碼付報酬,而不是測試,所以,我對此的價值觀是——測試越少越好,少到你對你的程式碼質量達到了某種自信。

相關推薦

加速Java應用開發速度3——單元/整合測試+CI

大家可能對如下情景比較熟悉: 如果開發過SSH的web專案,啟動伺服器可能會比較慢,有的專案甚至需要1分多鐘,甚至更多,這個啟動時間的等待一般就浪費了;在開發專案時,有些功能比較複雜,當時覺得思路特清晰,但是過了一段時間後,自己也忘了,完善功能時頻繁出現bug,降低開發速

Django 應用開發3

images alt doc include 新的 logs end esp eat 1.編寫第一個視圖 打開polls/view.py 利用一個URLconf將這個視圖映射到URL上。 首先先創建一個urls.py文件 編寫polls/urls.py 編寫m

Java應用開發中的SQL註入攻擊

包括 安全防護 sql註入 什麽 由於 應用程序 輸入數據 數據庫防火墻 進行 1. 什麽是SQL註入攻擊? SQL註入攻擊是黑客對數據庫進行攻擊的常用手段之一。隨著B/S模式應用開發的發展,使用這種模式編寫應用程序的程序員越來越多。但是由於程序員的水平及經驗參差不齊,相當

[HDFS_4] HDFS 的 Java 應用開發

() else stream ava 單元 技術 put 編寫 寫文件 0. 說明   在 IDEA下 進行 HDFS 的 Java 應用開發   通過編寫代碼實現對 HDFS 的讀寫操作 1. 流程   1.1 在項目下新建 Moudle   略   

基於Python的Web應用開發實戰——3 模板

要想開發出易於維護的程式,關鍵在於編寫形式簡潔且結構良好的程式碼。 當目前為止,你看到的示例都太簡單,無法說明這一點,但Flask檢視函式的兩個完全獨立的作用卻被融合在了一起,這就產生了一個問題。   檢視函式的作用很明確,即生成請求的響應。 如第2章中的示例,對簡單的請求來所,這就足夠了。

基於spring-boot的應用程式的單元+整合測試方案

概述 本文主要介紹單元測試、整合測試相關的概念、技術實現以及最佳實踐。 本文的demo是基於Java語言,Spring Boot構建的web應用。測試框架使用Junit,模擬框架使用mockito。 之前曾經總結過一篇文章:基於spring-boot的應用程式的單元測試方案,但是當時只是從技術實現的角度

安卓應用開發3)執行工程

由於Google是官方,所以本系列內容全部是掛外網,請參考者注意。如若打不開,請使用vpn。 Google官方教程 https://developer.android.com/training/basics/firstapp/creating-project 1. 連線硬體

Java應用開發的顛覆者:Spring Boot

有人說,Spring Boot的出現,讓Java迎來了又一春,它是Java應用開發的顛覆者,徹底改變了Java應用開發的模式。 以往我們做Spring應用開發,要知道配置哪些類來讓Hibernate和Spring一起工作,要知道如何配置view resolver來控制哪個模

31、你瞭解Java應用開發中的注入攻擊嗎?

安全是軟體開發領域永遠的主題之一,隨著新技術浪潮的興起,安全的重要性愈發凸顯出來,對於金融等行業,甚至可以說安全是企業的生命線。不論是移動裝置、普通 PC、小型機,還是大規模分散式系統,以及各種主流作業系統,Java 作為軟體開發的基礎平臺之一,可以說是無處不在,自然也就成為安全攻擊的首要

WebSphere MQ Java 應用開發簡單例項(上篇:客戶端模式開發)(client mode和binding mode)

1-背景知識 IBM MQ支援多種語言開發,本文主要是考慮Java應用開發,MQ提供了相關的Java類庫,可以很方便整合到Java應用中。 IBM MQ Java相關類庫允許Java應用直接與佇列管理器互動,或者連線MQ伺服器和客戶端進行互動。 類庫主要有

QtQuick桌面應用開發指導 3)實現UI和功能_B 4)動態管理Note物件_A

3.2 把Page Item和Marker Item繫結 之前我們實現了PagePanel元件, 使用了三個state來切換Page元件的opacity屬性; 這一步我們會使用Marker和MarkerPanel元件來實現頁面導航;  在原型階段,

OSGI企業應用開發(九)整合Spring和Mybatis框架(二)

上篇文章中,我們完成了在OSGI應用中整合Spring和Mybatis框架的準備工作,本節我們繼續Spring和Mybatis框架的整合。 一、解決OSGI整合Spring中的Placeholder問題 使用Spring框架的朋友應該都知道,我們可以在B

.NET Core 3.0 單元測試與 Asp.Net Core 3.0 整合測試

單元測試與整合測試 測試必要性說明 相信大家在看到單元測試與整合測試這個標題時,會有很多感慨,我們無數次的在實踐中提到要做單元測試、整合測試,但是大多數專案都沒有做或者僅建了專案檔案。這裡有客觀原因,已經接近交付日期了,我們沒時間做白盒測試了。也有主觀原因,面對業務複雜的程式碼我們不知道如何入手做單元測試,不

java單元/整合測試中使用Testcontainers

## 1.Testcontainers介紹: Testcontainers是一個Java庫,它支援JUnit測試,提供公共資料庫、SeleniumWeb瀏覽器或任何可以在Docker容器中執行的輕量級、一次性例項。 **測試容器使以下型別的測試更加容易:** **資料訪問層整合測試:** 使用MySQ

java框架】SpringBoot(3) -- SpringBoot整合Swagger2

1.SpringBoot web專案整合Swagger2 1.1.認識Swagger2 Swagger 是一個規範和完整的框架,用於生成、描述、呼叫和視覺化 RESTful 風格的 Web 服務。總體目標是使客戶端和介面文件系統作為伺服器以同樣的速度來更新。文件的介面方法,引數和模型緊密整合到伺服器端的程式

Java Web開發(五) JavaScript庫jQuery學習(3) jQuery與Ajax的應用(1)

  關於jQuery選擇器的知識這裡就先不介紹了,這裡向大家介紹一種比較厲害的技術,就是jQuery的Ajax應用,哈哈哈,我們當然先了解一些先進的技術啦,之後再為大家介紹jQuery選擇器和其他知識。 現在讓我們進入強大的jQuery的Ajax應用學習中吧。  

java電影站點開發經驗3

點擊 解決 百度谷歌 也會 阿裏 spa 都沒有 開發經驗 一個 上次講到了。站點有資源了。可是必需要點綴下站點,要不光有資源比較空的。最開始就是想到了給資源加入評論功能的。然後自己就向開發個評論功能。可是由於時間問題,並且本人也比較懶,就想在網上找找

Java進擊C#——應用開發之Linq和EF

了吧 -1 擴展 有一點 增刪改 adk 對象 structure mis 本章簡言 上一章筆者對於WinForm開發過程用到的幾個知識點做了講解。筆者們可以以此為開端進行學習。而本章我們來講一個跟ORM思想有關的知識點。在講之前讓我們想一下關於JAVA的hib

Java進擊C#——應用開發之WinForm開發

logs i++ mex 有時 數據 rst 流動 應用程序 dir 本章簡言 上一章筆者介紹了關於WinForm環境。這一章筆者將繼續講WinForm。只不過更加的面向開發了。事實就是在學習工具箱裏面的控件。對於WinForm開發來講,企業對他的要求並沒有那麽

Java進擊C#——應用開發之WinForm環境

png 程序員 esp renderer 復制 表示 com ont mode 本章簡言 上一章筆者講到關於IO文件操作類,了解如何處理文件流。從這一章開始筆者將講解相對比較高級的知識點。而本章筆者就對WinForm開發的知識點進行講解和引導。現在很多業務都是面