推ter的開源自動化測試工具:Diffy
集成測試的挑戰
隨著軟件系統的復雜性逐漸增加,微服務、面向服務的架構(Service-oriented architectures, SOA)等概念,越來越多的被應用到系統的設計當中,一同伴隨的結果就是系統組件逐漸增加。
對于測試活動而言,最底層的單元測試,主要測試目標是單一的功能模塊。它能夠確保每個組件自身業務邏輯的正確性,但是隨著系統組件依賴的增加,對單 一模塊的單元測試難度和成本都會上升。同時,單元測試覆蓋率的提升,只能確保系統各個組件的正確性,組件之間的集成測試仍然是必不可少的。
傳統集成測試的難點在于,對于每一個模塊,都會有幾個需要測試覆蓋的分支,隨著模塊的增加,這些測試分支的組合,將會呈現幾何級的增長(如圖1所示)。
圖1:隨著模塊增加,測試復雜度指數級增加
推ter公司發布的自動化測試工具Diffy,就是為了降低開發人員對這種復雜系統的測試成本。
相關廠商內容
回顧技術人到管理者的成功跨越
豌豆莢如何構建內容型APP客戶端
初創公司如何打破困局帶領團隊走上正軌
</div> </div> </div>相關贊助商

全球架構師峰會,12月18-19日,北京·國際會議中心,8折報名最后一周,立減1360元!
</div> </div> </div>Diffy簡介
Diffy是一個開源的自動化測試工具,它能夠自動檢測基于Apache Thrift或者基于HTTP的服務。使用Diffy,只需要進行簡單的配置,之后不需要再編寫測試代碼。
Diffy主要基于穩定版本和它的副本的輸出,對候選版本的輸出進行比較,以檢查候選版本是否正確。因此,Diffy首先假設候選版本應該和穩定版本有“相似”的輸出。即不論候選版本和穩定版本系統模塊是否相同,他們的最終輸出應該是“相似”的。
這里一直使用“相似”,而不是使用相同,這是因為相同請求可能會有一些Diffy不需要關心的干擾。比如:
- 響應中包含服務器生成的時間戳
- 代碼中使用了隨機數
- 系統服務間有條件競爭 </ul>
- 候選版本:該版本是待測版本,相對于生產環境版本有著跟新的代碼
- 穩定版本:該版本通常是已經上線版本,或者是已知功能正常的版本
- 穩定版本副本:該版本是穩定版本的副本,和穩定版本運行相同的代碼,主要用于排除噪聲 </ol>
- 原始區別為候選版本和穩定版本之間輸出的區別,其中可能會包含上述的噪聲
- 噪聲從穩定版本和其副本中獲得,如果兩個運行相同代碼的系統輸入相同輸出卻不同,則Diffy會認為這是開發人員不需要關心的噪聲。 </ul>
Diffy有自己的噪聲清理方式,確保這些噪聲不會影響最終的結果。
Diffy工作原理
在測試過程中,Diffy充當一個代理,它能夠將來源請求分發到不同版本的系統中去,通過對各個版本系統的輸出進行對比,做出最終的結論。
Diffy需要三個版本的系統,以實現它的噪聲過濾和對比功能,它們分別是:
整個運行流程為:
其中:
基于上述兩個區別集合,Diffy可以識別出候選版本和穩定版本真實的區別,這些區別很有可能就是一個缺陷。
當然,對于一個概率性出現隨機值,僅僅一次請求的結論可能是不準確的。例如對于一個50%概率出現true或者false的布爾值,則有50%的概 率會出現候選版本和穩定版本的不同,同時又會有50%的概率出現穩定版本和其副本出現不同(即將這個值認定為噪聲),最終會有25%的概率認為這是一個缺 陷。因為此時穩定版本和其副本值相同,候選版本和穩定版本值不同。因此,Diffy還會聚合原始區別和噪聲,當發現二者出現的概率類似的時候,會認定之前 識別出來的缺陷屬于誤報。
示例
最后,通過Diffy倉庫中的示例,來大致了解下Diffy的運行方式和過程:
步驟1:克隆源碼,并進行構建:
git clone https://github.com/推ter/diffy.git cd diffy ./sbt assembly
國內如果下載速度很慢,可以修改下sbt的鏡像,使用國內的鏡像:
#cat ~/.sbt/repositories [repositories] local osc: http://maven.oschina.net/content/groups/public/ oschina-ivy:http://maven.oschina.net/content/groups/public/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/ [type]s/[artifact](-[classifier]).[ext] sonatype-oss-releases maven-central sonatype-oss-snapshots
步驟2:啟動候選服務。這里直接使用example.sh,因此對應的候選服務地址為:http-candidate.herokuapp.com:80
步驟3:啟動基準服務(穩定版本)。這里直接使用example.sh,因此對應的候選服務地址為:http-primary.herokuapp.com:80
步驟4:啟動穩定版本副本。這里對應的是:http-secondary.herokuapp.com:80
步驟5:使用以下命令運行Diffy(example.sh):
java -jar diffy-server.jar \ -candidate='http-candidate.herokuapp.com:80' \ -master.primary='http-primary.herokuapp.com:80' \ -master.secondary='http-secondary.herokuapp.com:80' \ -service.protocol='http' \ -serviceName='My Service' \ -proxy.port=:8880 \ -admin.port=:8881 \ -http.port=:8888 \ -rootUrl='localhost:8888'
該命令指定了Diffy需要的三個版本對應的訪問地址,同時在8880端口開啟代理,8888端口開啟了結果訪問服務。現在可以通過訪問8880端 口,Diffy會將請求同時分發到三個版本的http服務上,然后記錄這三個http服務的返回值。通過訪問8888端口,就可以看見對這三個返回內容的 對比結果。命令執行輸出如下:
coolex scala-2.11 # java -jar diffy-server.jar \ > -candidate='http-candidate.herokuapp.com:80' \ > -master.primary='http-primary.herokuapp.com:80' \ > -master.secondary='http-secondary.herokuapp.com:80' \ > -service.protocol='http' \ > -serviceName='My Service' \ > -proxy.port=:8880 \ > -admin.port=:8881 \ > -http.port=:8888 \ > -rootUrl='localhost:8888' SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. I 0908 13:09:41.130 THREAD1: HttpMuxer[/admin/metrics.json] = com.推ter.finagle.stats.MetricsExporter(<function1>) I 0908 13:09:41.189 THREAD1: HttpMuxer[/admin/per_host_metrics.json] = com.推ter.finagle.stats.HostMetricsExporter(<function1>) I 0908 13:09:41.393 THREAD1: /admin => com.推ter.server.handler.SummaryHandler I 0908 13:09:41.394 THREAD1: /admin/server_info => com.推ter.finagle.Filter$$anon$2 I 0908 13:09:41.394 THREAD1: /admin/contention => com.推ter.finagle.Filter$$anon$2 I 0908 13:09:41.394 THREAD1: /admin/threads => com.推ter.server.handler.ThreadsHandler I 0908 13:09:41.394 THREAD1: /admin/threads.json => com.推ter.server.handler.ThreadsHandler I 0908 13:09:41.394 THREAD1: /admin/announcer => com.推ter.finagle.Filter$$anon$2 I 0908 13:09:41.395 THREAD1: /admin/dtab => com.推ter.finagle.Filter$$anon$2 I 0908 13:09:41.395 THREAD1: /admin/pprof/heap => com.推ter.server.handler.HeapResourceHandler I 0908 13:09:41.395 THREAD1: /admin/pprof/profile => com.推ter.server.handler.ProfileResourceHandler I 0908 13:09:41.395 THREAD1: /admin/pprof/contention => com.推ter.server.handler.ProfileResourceHandler I 0908 13:09:41.395 THREAD1: /admin/ping => com.推ter.server.handler.ReplyHandler I 0908 13:09:41.396 THREAD1: /admin/shutdown => com.推ter.server.handler.ShutdownHandler I 0908 13:09:41.396 THREAD1: /admin/tracing => com.推ter.server.handler.TracingHandler I 0908 13:09:41.396 THREAD1: /admin/events => com.推ter.server.handler.EventsHandler I 0908 13:09:41.396 THREAD1: /admin/logging => com.推ter.server.handler.LoggingHandler I 0908 13:09:41.397 THREAD1: /admin/metrics => com.推ter.server.handler.MetricQueryHandler I 0908 13:09:41.397 THREAD1: /admin/clients/ => com.推ter.server.handler.ClientRegistryHandler I 0908 13:09:41.397 THREAD1: /admin/servers/ => com.推ter.server.handler.ServerRegistryHandler I 0908 13:09:41.397 THREAD1: /admin/files/ => com.推ter.server.handler.ResourceHandler I 0908 13:09:41.397 THREAD1: /admin/registry.json => com.推ter.server.handler.RegistryHandler I 0908 13:09:41.403 THREAD1: Serving admin http on 0.0.0.0/0.0.0.0:8881 I 0908 13:09:41.478 THREAD1: Finagle version 6.28.0 (rev=de123b8f9d074c4e345ebd67e1a0e870bb921544) built at 20150827-162434 I 0908 13:09:43.010 THREAD1: networkaddress.cache.ttl is not set, DNS cache refresh turned off I 0908 13:09:43.507 THREAD1: Tracer: com.推ter.finagle.zipkin.thrift.SamplingTracer I 0908 13:09:43.810 THREAD1: zipkin-tracer resolved to Addr.Bound, current size=1 I 0908 13:09:43.811 THREAD1: candidate resolved to Addr.Bound, current size=1 I 0908 13:09:43.811 THREAD1: primary resolved to Addr.Bound, current size=1 I 0908 13:09:43.811 THREAD1: secondary resolved to Addr.Bound, current size=1 I 0908 13:09:43.885 THREAD1: Scheduling com.推ter.diffy.workflow. FunctionalReport at 2015-09-08 13:09:43 +0000
步驟6:發送一些請求,讓Diffy來記錄和分析:
curl localhost:8880/json curl localhost:8880
步驟7:通過localhost:8888查看結果:
可以看見剛才訪問的兩個地址都已經被記錄,同時由于第二個地址增加了時間戳,所以原始對比結果認為他們是不相同的。
(點擊放大圖像)
點擊對比失敗的地方,可以展示出兩次對比不同之處:
(點擊放大圖像)
可以看見,該請求兩個版本的不同是由時間戳導致的。如果選擇“排除噪聲”,那么最終結論會變成相同。
(點擊放大圖像)
來自:http://www.infoq.com/cn/articles/diffy-推ter-open-source-automation-testing-tool