將Apache火花與黃瓜Behavioral-Driven發展< /h1>
2017年6月2日
在公司博客上< /a>
2017年6月2日 在公司博客上< /a>
這是一篇博客FIS全球< /a>
數據處理中最困難的場景之一是確保是正確的和預期的數據。我們可以采用多種測試方法來解決這個問題,但通常這些方法限製合作在一個團隊中,不要直接回答這個問題,“我如何證明一切工作?”< /p>
使用行為驅動開發(BDD)模式來發展我們的轉換可以使整個團隊參與和關注結果。黃瓜項目(Cucumber.io< /a>)提供了一個易於使用的框架實現BDD方法大多數項目。< /p>
當結合Apache火花< /a>數據處理和黃瓜(Cucumber.io),我們做出一個令人信服的組合,維護係統的規模和能證明數據被正確處理。這不僅使開發團隊,可以帶來其他手段成功地測試場景,通常很難證明如機器學習。< /p>
今天的大多數軟件以敏捷的方式是合作完成。beplay娱乐ios這意味著我們得到一個健康的專業人士有不同的視角試圖扮演構建軟件的團隊運動。項目遭受的最大問題之一是低質量工程師和領域專家之間的溝通。< /p>
黃瓜讓我們寫我們的軟件在一個簡單的一部分,基於語言的方法,使所有的團隊成員能夠輕鬆閱讀單元測試。我們的重點是詳細描述我們希望係統返回的結果。非技術團隊的成員可以很容易地創建、讀取,並驗證係統的測試。< /p>
經常Apache火花是一個組件在許多處理數據,這可以鼓勵多個測試框架。黃瓜可以幫助我們提供了一個一致的單元測試策略當項目可能會延長過去Apache火花進行數據處理。而不是混合子項目之間的不同的單元測試策略,我們創建一個可讀的敏捷驗收框架。這是創造一種“自動化驗收測試”。< /p>
最重要的是,我們能夠在開發過程中創建“活文檔”產生。而不是一個單獨的文檔過程中,單元測試形成一個可讀的文檔,可以向外界可讀。每次更新代碼,文檔更新。這是一個真正的雙贏。< /p>
為了取得成功,我們需要一個配方。< /p>
成功的BDD數據轉換項目< /strong> 首先,添加到java項目Maven文件。< /p>
我們還需要設置Junit跑步者認識到黃瓜。< /p>
為了增加樂趣,依賴注入可以添加一些香料配方。在Scala可以作為項目用於火花,我們發現黃瓜的Java項目工作更好。JVM可以讓我們與每個語言的接口進行交互。< /p>
我們需要創建一個feature< /em>文件,這是一個文件用一個名為小黃瓜的可執行語言。這將有一個簡單的格式:< /p>
特點:< /strong>吃好東西 我們可以注意到有一些關鍵詞:,,然後,功能,和場景。這些關鍵詞可以以任何方式使用,周圍的標準化幾個關鍵詞是使可讀性。特性文件真正告訴讀者。< /p>
你需要混合步中定義。步驟定義是如何或基本的Java代碼。一個很酷的好處是,黃瓜可以給你放入步驟定義的方法基於特征文件。< /p>
最後也是最重要的一步是寫一個數據轉換。為此,我們需要我們的殺手成分。Apache火花。< /p>
什麼使Apache火花殺手在我們的數據處理是根本性的變化。到目前為止,數據處理是一個批處理的過程。我們會把數據,等待一段時間,然後把它處理。過程的最後,這可能是一段時間的等待,我們可以驗證結果。在我們周圍有許多類型的“單元測試”這種方法,但是都很弱,常常忽略了。質量係統中被認為和沒有保證的。< /p>
與Apache火花< /em>,我們能夠立即執行數據處理和驗證它。我們保證將邊界速度。我們能夠用統一的測試框架的數據處理,保證速度,並立即知道結果。我們現在可以證明和保證質量。當你擴展這個火花的工具去做機器學習任務,傳統上非常努力並及時來證明,快速行動加速的能力。< /p>
我們選擇使用一個非常簡單的,但是也許是常見的場景。數據生產者生成數據時,我們常常關心當數據生成。對於我們的目的,我們將假定時間總是記錄在時代(Unix時間)和機器生成是完全符合所有已知的時間同步。< /p>
我們的消費者,用戶在牆壁,關心有時間更可讀的方式。為簡單起見,我們將假設他們總是希望時間發生在太平洋夏令時。< /p>
黃瓜是我們的語言解析器,它提供了一個輕量級結構記錄的可執行的規範。其主要目標是可讀性。對於這個場景,我們將編寫單元測試如下:< /p>
這就是真正的魔法。小黃瓜文件編寫,顯然可說明的我們的團隊。所以我們需要執行它,找出我們顯然可以理解的文件將執行單元測試。如果我們運行它,我們將得到一些輸出:< /p>
黃瓜框架自動生成我們需要實現的方法。現在我們需要一起鉤特性文件的實際調用運行單元測試。我們來填滿它。< /p>
這似乎是額外的工作隻是實現我們在JUnit單元測試。但是,如果我們有在JUnit中實現我們的測試將隻對係統的開發人員完全可讀。鉤子真的沒有很多額外的工作,通過創建存根和黃瓜實際上幫助了需要完成的工作。< /p>
我們做的是創建一個方法文檔係統的用戶係統以敏捷的方式。我們可以把額外的工作文檔的其他地方,因為我們已經完成了工作。我們也可以做流程的變化,問我們的專家我們人寫小黃瓜質量文件,然後填入一步鉤子。< /p>
現在讓我們把這個場景擴展到磚。磚是一個很好的平台數據科學家通過易於使用的筆記本電腦環Beplay体育安卓版本境。真正價值通過在一個平台建造的團隊,創建Apache火花。Beplay体育安卓版本< /p>
我們的數據,科學家們可以花大量的時間準備數據。他們正在申請公司的業務規則和清潔的數據準備。我們開始失去這些統計碩士的價值通過他們深陷在細節。數據科學家應該在關注解鎖見解的數據,但通常我們有特定於業務邏輯表示的數據是如何形成的。我們希望可行的見解,而不是鼓勵的spreadmarts不同觀察基於所使用的製備技術。< /p>
現在提出我們上麵的場景。我們可以把數據對齊科學家編纂製備規則compilable jar。這種邏輯可以很容易地纏繞在黃瓜。單元測試的好處是美妙的,但是我們現在有一個新的一雙眼睛看著我們的代碼。這些眼睛可能無法讀取Java作為他們的首選語言晦澀難懂。因為我們使用通用語言,數據科學家現在可以看看模塊構建和測試的方式。他們可以構建和驗證我們的場景!< /p>
磚的筆記本電腦,我們可以通過加載和準備數據編譯jar並切換到一個不同的科學家們的首選語言。因為磚允許我們把罐子一個集群,我們可以確保業務邏輯測試和理解,然後擴展成磚。這允許一個敏捷過程發現新的數據值,同時確保複雜的業務邏輯測試。< /p>
步驟:< /p>
磚是一種強大的統一的數據處理平台,培育發展過程中一個協作環境。beplay娱乐iosBeplay体育安卓版本我們不僅展示了我們可以統一數據科學家的昂貴的數據準備工作,也使準備工作很容易驗證。< /p>
我們也能夠統一多個數據處理組件支持Apache的火花在一個可讀的單元測試框架和產生“活文檔”係統是如何工作的。每個人都從開發到測試到行政利益相關者現在可以閱讀和協作係統測試和理解其行為。< /p>
的真正價值時,文檔在開發時創建,而不是作為一個單獨的進程。您的項目成為真正麵向結果完全敏捷的方式。< /p>
你有興趣聽到更多關於火花和BDD測試?來參加我們的會議在火花峰會上2017 !< /a>為什麼黃瓜和Apache火花呢?< /h2>
我們成功的秘訣< /h2>
1杯Apache的火花
1杯Cucumber.io
2杯IntelliJ(替代與Eclipse如果你發現IntelliJ太鹹)
½杯磚。< /p>
< br / > info.cukes< /span>cucumber-java81.2.5測試
包mypackage;< /span>進口< /span>cucumber.api.CucumberOptions;< /span>進口< /span>cucumber.api.junit.Cucumber;< /span>進口< /span>org.junit.runner.RunWith;< /span>@RunWith (< /span>黃瓜。< /span>類< /span>)< /span>@CucumberOptions (< /span>插件= {< /span>“漂亮”< /span>,< /span>“html:目標/黃瓜”< /span>}< /span>)< /span>公共< /span>類< /span>RunCukesTest< /span>{< /span>}< /span>
場景:< /strong>吃一些冰激淩
給定一個< /strong>冰淇淋甜筒
當< /strong>我吃冰淇淋蛋卷
然後< /strong>我應該高興< /p>
數據處理的情況< /h2>
類< /span>ExtractionClass< /span>(< /span>sparkSession< /span>:< /span>SparkSession< /span>)< /span>{< /span>val TIMESTAMP_FORMAT =< /span>“yyyy-MM-dd HH: mm: ss”< /span>def< /span>RunExtractJob< /span>(sourceFilePath:字符串,destinationFilePath:字符串)< /span>:單元< /span>= {< /span>val sourceDataFrame: DataFrame = GetJsonDataFrame (sourceFilePath)val extractedDataFrame: DataFrame = ExtractDataFrame (sourceDataFrame)SaveJsonDataFrame (extractedDataFrame destinationFilePath)}def< /span>GetJsonDataFrame< /span>(filePath:字符串)< /span>:DataFrame< /span>= {< /span>sparkSession.read.json (filePath)}def< /span>ExtractDataFrame< /span>(dataFrame: dataFrame)< /span>:DataFrame< /span>= {< /span>進口< /span>sparkSession.implicits._< /span>
dataFrame.withColumn (< /span>“timestampGmt”< /span>from_unixtime(美元)< /span>“unixTimestamp”< /span>))< /span>.withColumn (< /span>“timestampLtz”< /span>,< /span>date_format(美元)< /span>“unixTimestamp”< /span>+(美元)< /span>“timezoneOffset”< /span>*< /span>60< /span>*< /span>60< /span>).cast (TimestampType) TIMESTAMP_FORMAT))< /span>}def< /span>SaveJsonDataFrame< /span>(dataFrame: dataFrame filePath:字符串)< /span>:單元< /span>= {< /span>dataFrame.write.json (filePath)}}< /code>
當,,,< /h2>
@Extraction< /span>@TempFileCleanup< /span>@ApacheSpark< /span>特點:Json日誌提取過程背景:一般< /span>係統< /span>設置< /span>考慮到< /span>係統< /span>是< /span>在< /span>UTC< /span>時間< /span>場景:基本的提取< /span>的< /span>時代< /span>時間< /span>成< /span>可讀的< /span>當地的< /span>時間< /span>區< /span>鑒於有< /span>是< /span>一個文件“srcFolder / example.json”< /span>與< /span>以下行:< /span>|< /span>{“logId”:< /span>1< /span>“unixTimestamp”:< /span>1459482142< /span>“timezoneOffset”:< /span>6< /span>}< /span>|< /span>|< /span>{“logId”:< /span>2< /span>“unixTimestamp”:< /span>1459482142< /span>“timezoneOffset”:< /span>2< /span>}< /span>|< /span>當< /span>的< /span>方法< /span>RunExtractJob得到< /span>被稱為< /span>與< /span>|< /span>SourceFolder< /span>|< /span>srcFolder< /span>/ * |< /span>| DestinationFolder | dstFolder |< /span>然後會有一個“_SUCCESS”文件在“dstFolder”文件夾中< /span>和文件夾“dstFolder”將json文件完全DataFrame行如下:< /span>| logId | unixTimestamp | timezoneOffset | timestampGmt | timestampLtz |< /span>| 1 | 1459482142 | 6 | 1459482142 03:42:22 | 2016-03-31 21:42:22 |< /span>| 2 | 1459482142 | 2 | 1459482142 03:42:22 | 2016-04-01 01:42:22 |< /span>
走起來< /h2>
@Given (“UTC時間$ ^係統”)< /span>公共< /span>無效< /span>the_system_is_in_UTC_time< /span>()< /span>拋出< /span>Throwable< /span>{< /span>/ /寫代碼,把上麵的短語變成具體的行動< /span>扔< /span>新< /span>PendingException ();< /span>}< /code>
公共< /span>類< /span>ExtractionStepDefinitions< /span>{< /span>@< /span>鑒於< /span>(< /span>“在UTC時間$ ^係統”< /span>)< /span>公共< /span>無效< /span>theSystemIsInGMTTime< /span>()< /span>拋出< /span>Throwable< /span>{< /span>時區< /span>。< /span>setDefault< /span>(< /span>TimeZone.getTimeZone (< /span>“UTC”< /span>)< /span>);< /span>}< /span>@< /span>鑒於< /span>(< /span>“^有文件\”([^ \]*)\”以下行:$”< /span>)< /span>公共< /span>無效< /span>thereIsAFileWithTheFollowingLines< /span>(< /span>字符串propertiesPath,< /span>列表< /span>行< /span>)< /span>拋出< /span>Throwable< /span>{< /span>文件< /span>文件< /span>=< /span>新< /span>文件< /span>(< /span>Helpers.getTempPath (< /span>propertiesPath< /span>)< /span>);< /span>文件< /span>。< /span>getParentFile< /span>()。< /span>mkdir< /span>();< /span>PrintWriter< /span>作家< /span>=< /span>新< /span>PrintWriter< /span>(< /span>file.getAbsolutePath (< /span>),< /span>“utf - 8”< /span>);< /span>為< /span>(< /span>字符串< /span>str< /span>:行< /span>){< /span>作家< /span>。< /span>println< /span>(< /span>str< /span>.trim (< /span>)< /span>);< /span>}< /span>作家< /span>。< /span>關閉< /span>();< /span>}< /span>@< /span>當< /span>(< /span>“^方法(RunExtractJob | RunExtractJobV2)被稱為美元”< /span>)< /span>公共< /span>無效< /span>theMethodRunExtractJobGetsCalledWith< /span>(< /span>字符串jobName, Map < String字符串=< /span>”“< /span>>參數< /span>)< /span>拋出< /span>Throwable< /span>{< /span>如果< /span>(< /span>jobName.compareTo (< /span>“RunExtractJob”< /span>)= =< /span>0< /span>){< /span>新< /span>ExtractionClass< /span>(< /span>Helpers.testSparkSession< /span>)。< /span>RunExtractJob< /span>(< /span>Helpers.getTempPath (< /span>arguments.get (< /span>“SourceFolder”< /span>)< /span>),< /span>Helpers.getTempPath (< /span>arguments.get (< /span>“DestinationFolder”< /span>)< /span>)< /span>);< /span>}< /span>其他的< /span>如果< /span>(< /span>jobName.compareTo (< /span>“RunExtractJobV2”< /span>)= =< /span>0< /span>){< /span>新< /span>ExtractionClassV2< /span>(< /span>Helpers.testSparkSession< /span>)。< /span>RunExtractJob< /span>(< /span>Helpers.getTempPath (< /span>arguments.get (< /span>“SourceFolder”< /span>)< /span>),< /span>Helpers.getTempPath (< /span>arguments.get (< /span>“DestinationFolder”< /span>)< /span>),< /span>arguments.get (< /span>“TimezoneOffset”< /span>)< /span>);< /span>}< /span>其他的< /span>{< /span>扔< /span>新< /span>PendingException< /span>();}< /span>…< /span>< /< /span>字符串< /span>>< /span>
用黃瓜磚< /h2>
mvn包< /code>好處的運行黃瓜測試,確保數據質量。< /li>
經驗教訓< /h2>