将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>
今天的大多数软件以敏捷的方式是合作完成。这意味着我们得到一个健康的专业人士有不同的视角试图扮演构建软件的团队运动。项目遭受的最大问题之一是低质量工程师和领域专家之间的沟通。< /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>
现在让我们把这个场景扩展到砖。砖是一个很好的平台数据科学家通过易于使用的笔记本电脑环bob体育客户端下载境。真正价值通过在一个平台建造的团队,创建Apache火花。bob体育客户端下载< /p>
我们的数据,科学家们可以花大量的时间准备数据。他们正在申请公司的业务规则和清洁的数据准备。我们开始失去这些统计硕士的价值通过他们深陷在细节。数据科学家应该在关注解锁见解的数据,但通常我们有特定于业务逻辑表示的数据是如何形成的。我们希望可行的见解,而不是鼓励的spreadmarts不同观察基于所使用的制备技术。< /p>
现在提出我们上面的场景。我们可以把数据对齐科学家编纂制备规则compilable jar。这种逻辑可以很容易地缠绕在黄瓜。单元测试的好处是美妙的,但是我们现在有一个新的一双眼睛看着我们的代码。这些眼睛可能无法读取Java作为他们的首选语言晦涩难懂。因为我们使用通用语言,数据科学家现在可以看看模块构建和测试的方式。他们可以构建和验证我们的场景!< /p>
砖的笔记本电脑,我们可以通过加载和准备数据编译jar并切换到一个不同的科学家们的首选语言。因为砖允许我们把罐子一个集群,我们可以确保业务逻辑测试和理解,然后扩展成砖。这允许一个敏捷过程发现新的数据值,同时确保复杂的业务逻辑测试。< /p>
步骤:< /p>
砖是一种强大的统一的数据处理平台,培育发展过程中一个协作环境。bob体育客户端下载我们不仅展示了我们可以统一数据科学家的昂贵的数据准备工作,也使准备工作很容易验证。< /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>