<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Neoscript&#39;s Blog</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://neoscript99.github.io/"/>
  <updated>2019-01-15T09:15:50.593Z</updated>
  <id>https://neoscript99.github.io/</id>
  
  <author>
    <name>neoscript</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>指标模型运算系统的意义与实现</title>
    <link href="https://neoscript99.github.io/2018/01/04/2018/matrix-rule-intro/"/>
    <id>https://neoscript99.github.io/2018/01/04/2018/matrix-rule-intro/</id>
    <published>2018-01-04T02:16:29.000Z</published>
    <updated>2019-01-15T09:15:50.593Z</updated>
    
    <content type="html"><![CDATA[<h1 id="指标模型的概念"><a href="#指标模型的概念" class="headerlink" title="指标模型的概念"></a>指标模型的概念</h1><ul><li>关键指标分析是一种常用的业务分析手段</li><li>指标可分为<strong><code>基础指标</code></strong>和<strong><code>计算指标</code></strong></li><li><strong><code>基础指标</code></strong>是指当前业务系统能够直接获取的指标，比如<strong><code>客户评级模型</code></strong>中客户的<strong><code>年龄、学历、性别、总借款次数</code></strong>等等</li><li><strong><code>计算指标</code></strong>是指通过定义计算公式，从其它指标计算得出的指标，比如：<ul><li><strong><code>性别得分</code></strong>这个指标的公式为<strong><code>如果 (${性别} == &quot;女&quot;) 60分 否则 40分</code></strong></li><li><strong><code>基本信息汇总得分</code></strong>这个指标的公式为<strong><code>${性别得分}×20% + ${年龄得分}×20% + ${学历得分}×60%</code></strong></li><li><strong><code>客户评级最终得分</code></strong>这个指标的公式为<strong><code>${基本信息汇总得分}×30% + ${业务统计信息得分}×40% + ${征信信息得分}×30%</code></strong></li></ul></li><li>通过上面列举的计算公式可以看出，指标模型的指标之间存在非常复杂的多层依赖关系<ul><li><strong><code>性别得分</code></strong>依赖<strong><code>性别</code></strong></li><li><strong><code>基本信息汇总得分</code></strong>依赖<strong><code>性别得分</code></strong>、<strong><code>年龄得分</code></strong>和<strong><code>学历得分</code></strong></li><li><strong><code>客户评级最终得分</code></strong>依赖<strong><code>基本信息汇总得分</code></strong>、<strong><code>业务统计信息得分</code></strong>、和<strong><code>征信信息得分</code></strong></li></ul></li><li>所以，某一业务领域的<strong><code>指标模型</code></strong>包含该业务领域设定的关键指标以及它们之间的计算关系。</li></ul><blockquote><p>文章后半段有系统演示，先聊聊概念和意义</p></blockquote><h1 id="建立指标模型的意义"><a href="#建立指标模型的意义" class="headerlink" title="建立指标模型的意义"></a>建立指标模型的意义</h1><ul><li>首先当然是满足当前的指标定义、计算、分析、展现需求（虽然普通的报表系统也能实现指标统计，但指标模型的真正价值在于对持续优化过程的支持和促进）</li><li><strong>业务人员对指标的多层计算过程有了一个全面的了解</strong></li><li><strong>指标的计算公式能对应需求的具体细节，业务需求不再需要文档来维护，可以直接配置到系统上</strong>（解决<em>需求文档和代码脱节</em>的问题）</li><li><strong>用领域专用语言（DSL）定义计算公式，普通业务人员也能很好的理解，可以自行维护</strong></li><li><strong>通过以上几点，业务人员对需求可进行较好的把控，有利于指标模型的逐步优化完善</strong></li><li><strong>通过指标模型的不断演进，模型所对应的业务领域的分析维度逐步清晰，再加上历史数据的积累，这时再以这些指标为基础，去做大数据分析、机器学习就水到渠成了</strong></li><li><strong>《三体》的“维度攻击”是近期非常热门的一个概念，新维度的发现需要数据的积累和一个不断试错的过程，而如何让业务、技术人员有目标性的去寻找新的维度，建立逻辑清晰、易配置、可扩展的指标模型是非常必要的手段。</strong></li></ul><blockquote><p>一个指标不一定就是一个有重要意义的维度，但不去建立指标模型、不去测试指标数据，那永远也找不到新的维度。比如假设一批借款数据，其中女性借款人的不良率是0.5%，男性是1.2%，差距非常大；如果没有建立包含<strong><code>性别</code></strong>这个指标的指标模型，就不会想到用指标的思维来分析数据；而更进一步，可能<strong><code>性别+学历</code></strong>、<strong><code>性别+年龄+学历</code></strong>组合计算之后，会有一个更加重要的维度出现，如果不先建立指标模型，这些都难以发现。</p></blockquote><blockquote><p>缺少传统业务分析积累、缺少业务人员的深度参与、无演进过程的大数据分析系统都将只是花架子。</p></blockquote><h1 id="系统功能模块：指标模型定义"><a href="#系统功能模块：指标模型定义" class="headerlink" title="系统功能模块：指标模型定义"></a>系统功能模块：指标模型定义</h1><ul><li>指标模型管理</li><li>指标管理（一个模型包括多个指标，指标的依赖限定于同一模型内）</li><li>指标计算公式的维护</li><li>解析指标计算公式的变量（支持普通、通配符、正则表达式变量），建立指标的依赖关系</li><li>指标值、公式的有效性检查</li><li>指标依赖死循环的检查</li><li>报表定义（每张报表包含多个指标，满足多业务场景的分析需求）</li></ul><h1 id="系统功能模块：指标模型运算"><a href="#系统功能模块：指标模型运算" class="headerlink" title="系统功能模块：指标模型运算"></a>系统功能模块：指标模型运算</h1><ul><li>运算对象管理（不同模型有不同类型的运算对象，如<strong><code>客户评级模型</code></strong>是<strong><code>客户</code></strong>、<strong><code>股票分析模型</code></strong>是<strong><code>股票</code></strong>、<strong><code>软件代码质量评价模型</code></strong>就是<strong><code>程序员</code></strong>）</li><li>运算批次发起（选择运算对象，发起一次指标运算批次）</li><li>基础指标录入，录入方式包括：<ul><li>自动导入 - 系统建设完善、基础指标可从已有业务系统、第三方接口抽取</li><li>手工导入 - 无完善信息系统，目前只能通过较人工的方式零散的拿到数据，先手工合并到excel表格，再进行批量导入</li><li>直接录入 - 适合基础指标较少的模型，在系统界面逐个录入</li></ul></li><li>基础指标录入值完整性、有效性检查</li><li>以上检查通过后，可发起模型运算</li><li>运算完成，得到所有<strong><code>计算指标</code></strong>结果</li></ul><h1 id="系统功能模块：结果展现"><a href="#系统功能模块：结果展现" class="headerlink" title="系统功能模块：结果展现"></a>系统功能模块：结果展现</h1><ul><li><strong><code>指标关系图</code></strong>，把单个运算对象的所有指标计算结果和多层依赖关系通过关系图的方式展示出来，非常直观的分析了计算过程和结果</li><li><strong><code>模型报表</code></strong>，根据模型定义中设置的报表，集中展示多个运算对象的运算结果，或单个运算对象的多次运算结果</li><li><strong><code>指标对比雷达图</code></strong>，用雷达图同时展示多个运算批次的报表指标值，可针对<strong><code>多对象多批次</code></strong>和<strong><code>单对象多批次</code></strong>的对比分析</li></ul><h1 id="系统演示：整体功能菜单"><a href="#系统演示：整体功能菜单" class="headerlink" title="系统演示：整体功能菜单"></a>系统演示：整体功能菜单</h1><p><img src="AllMenu.png" alt=""></p><blockquote><p>下有大量图片，PC端查看效果更佳</p></blockquote><h1 id="系统演示：指标模型定义"><a href="#系统演示：指标模型定义" class="headerlink" title="系统演示：指标模型定义"></a>系统演示：指标模型定义</h1><h2 id="指标模型管理"><a href="#指标模型管理" class="headerlink" title="指标模型管理"></a>指标模型管理</h2><ul><li>新增 - 增加一个指标模型</li><li>克隆 - 复制一个模型及其所有指标，这样可以支持模型的多版本管理，平滑升级</li><li>修改 - 修改模型名称和说明</li><li>删除选中 - 删除所选模型及其所有指标，以及所有运算批次数据</li></ul><p><img src="KpiModel.png" alt=""></p><h2 id="指标配置-基础指标"><a href="#指标配置-基础指标" class="headerlink" title="指标配置 - 基础指标"></a>指标配置 - 基础指标</h2><ul><li>基础指标是指需要外部导入、录入的原始业务数据</li><li><strong>测试值</strong>，是用来检查指标计算关系配置是否准确用的<blockquote><p>例如这两个指标：<strong><code>户籍认证</code></strong>，<strong><code>户籍认证得分</code></strong>（计算公式为：<strong><code>${户籍认证} == &#39;是&#39; ? 100 : 0</code></strong>）；如果<strong><code>户籍认证</code></strong>的测试值设置的是<strong><code>否</code></strong>，那么<strong><code>户籍认证得分</code></strong>的测试必须设置为<strong><code>0</code></strong>，和计算公式的结果一致</p></blockquote></li><li>通过配置<strong><code>测试值</code></strong>，可以检查整个<em>指标模型</em>的配置是否正确，所以后面会有<strong><code>检查测试值</code></strong>这个功能<br>  <img src="KpiConfig-01.png" alt=""></li></ul><h2 id="指标配置-计算指标"><a href="#指标配置-计算指标" class="headerlink" title="指标配置 - 计算指标"></a>指标配置 - 计算指标</h2><ul><li>计算公式通过变量来依赖其它指标（变量分为普通、通配符、正则表达式3种，<strong><code>下图1为普通变量</code></strong>，<strong><code>图2为通配符变量</code></strong>）<br>  <img src="KpiConfig-02.png" alt="图1"><br>  <img src="KpiConfig-03.png" alt="图2"></li></ul><h2 id="指标值、公式的有效性检查"><a href="#指标值、公式的有效性检查" class="headerlink" title="指标值、公式的有效性检查"></a>指标值、公式的有效性检查</h2><ul><li>检查测试值 - 全部通过，所有公式配置无误，所有测试值都跟计算结果匹配<br>  <img src="KpiCheck-01.png" alt=""></li><li>检查测试值 - 部份测试值跟计算结果不匹配<br>  <img src="KpiCheck-02.png" alt=""></li><li>检查测试值 - <strong><code>认证信息得分</code></strong>公式配置错误导致计算出错，同时导致所有直接、间接依赖它的指标也无法计算<br>  <img src="KpiCheck-03.01.png" alt=""><br>  <img src="KpiCheck-03.02.png" alt=""></li></ul><h2 id="指标依赖死循环的检查"><a href="#指标依赖死循环的检查" class="headerlink" title="指标依赖死循环的检查"></a>指标依赖死循环的检查</h2><ul><li>正确的依赖关系是这样的：<strong><code>客户评级结果</code></strong>依赖<strong><code>审核信息得分</code></strong>依赖<strong><code>年龄得分</code></strong>，<strong><code>年龄得分</code></strong>只依赖<strong><code>年龄</code></strong>这个基础指标<br>  <img src="KpiDeadLock-01.png" alt=""><br>  <img src="KpiDeadLock-02.png" alt=""><br>  <img src="KpiDeadLock-03.png" alt=""></li><li>为了测试死循环，让<strong><code>年龄得分</code></strong>依赖<strong><code>客户评级结果</code></strong><br>  <img src="KpiDeadLock-04.png" alt=""></li><li>检查测试值 - 提示死循环依赖<br>  <img src="KpiDeadLock-05.png" alt=""></li></ul><h2 id="报表定义"><a href="#报表定义" class="headerlink" title="报表定义"></a>报表定义</h2><ul><li>系统支持自定义报表</li><li>根据不同业务视角，可选取部份指标组合成一个报表，进行更有针对性的分析</li><li>一个指标对应一个报表列，并支持排序</li></ul><p><img src="KpiReportConfig-01.png" alt=""></p><h1 id="系统演示：指标模型运算"><a href="#系统演示：指标模型运算" class="headerlink" title="系统演示：指标模型运算"></a>系统演示：指标模型运算</h1><h2 id="运算对象管理、运算批次发起"><a href="#运算对象管理、运算批次发起" class="headerlink" title="运算对象管理、运算批次发起"></a>运算对象管理、运算批次发起</h2><p><img src="KpiBatchTarget.png" alt=""></p><ol><li>先通过右键菜单进行“运算对象”的增删改查操作</li><li>然后选择要“发起运算”的对象，可多选</li><li>再通过右上角的下拉列表选择运算模型</li><li>最后点击“发起运算”按钮进行发起。（如图所示，“个人客户AA、BB、CC”将同时发起“个人客户评级v1.0”模型运算）</li><li>同一个“运算对象”可以多次发起运算，得到不同时间点的运算结果，并可以对不同时间点的运行结果进行比较，更加深入的分析单个对象的数据变化过程</li></ol><h2 id="基础指标录入"><a href="#基础指标录入" class="headerlink" title="基础指标录入"></a>基础指标录入</h2><ul><li>导入步骤<br>  <img src="KpiBatchImport-01.png" alt=""></li><li>Excel模板演示（每个模型有对应的模板，并且会根据模型指标配置自动生成，不需要单独维护模板）<br>  <img src="KpiBatchImport-02.png" alt=""></li><li>导入完成，所有指标值有效，可提交运算，提交后系统后台将启动任务，进行所有计算指标的计算<br>  <img src="KpiBatchImport-03.png" alt=""></li><li>导入完成，部份指标值无效，需完善后才能提交运算<br>  <img src="KpiBatchImport-04.png" alt=""></li></ul><h2 id="运算状态监控"><a href="#运算状态监控" class="headerlink" title="运算状态监控"></a>运算状态监控</h2><p><img src="KpiBatchStatus.png" alt=""></p><ul><li><code>发起</code>，为初始状态，导入基础指标之后可提交模型运算</li><li><code>运算中</code>，系统后台计算任务正在运行</li><li><code>成功</code>，模型计算完成，可以查看结果</li><li><code>作废</code>，取消的运算批次</li></ul><h1 id="系统功能模块：结果展现-1"><a href="#系统功能模块：结果展现-1" class="headerlink" title="系统功能模块：结果展现"></a>系统功能模块：结果展现</h1><h2 id="指标关系图"><a href="#指标关系图" class="headerlink" title="指标关系图"></a><strong><code>指标关系图</code></strong></h2><p><img src="KpiBatchGraph.png" alt=""></p><h2 id="模型报表"><a href="#模型报表" class="headerlink" title="模型报表"></a><strong><code>模型报表</code></strong></h2><ul><li><strong><code>业务统计指标报表</code></strong>配置回顾<br>  <img src="KpiBatchReport-01.png" alt=""></li><li><strong><code>业务统计指标报表</code></strong>将实时对应<strong><code>报表定义</code></strong>的配置<br>  <img src="KpiBatchReport-02.png" alt=""></li><li><strong><code>关键指标报表</code></strong><br>  <img src="KpiBatchReport-03.png" alt=""></li></ul><h2 id="指标对比雷达图"><a href="#指标对比雷达图" class="headerlink" title="指标对比雷达图"></a><strong><code>指标对比雷达图</code></strong></h2><h3 id="多个运算对象的指标数据对比"><a href="#多个运算对象的指标数据对比" class="headerlink" title="多个运算对象的指标数据对比"></a>多个<strong><code>运算对象</code></strong>的指标数据对比</h3><ul><li><strong><code>业务统计指标报表</code></strong>的雷达图<br>  <img src="KpiBatchReportRadar-01.png" alt=""></li><li><strong><code>关键指标报表</code></strong>的雷达图<br>  <img src="KpiBatchReportRadar-02.png" alt=""></li></ul><h3 id="单个运算对象的多次运算数据对比"><a href="#单个运算对象的多次运算数据对比" class="headerlink" title="单个运算对象的多次运算数据对比"></a>单个<strong><code>运算对象</code></strong>的多次运算数据对比</h3><blockquote><p>可用于分析同一个<strong><code>运算对象</code></strong>在不同业务范围的数据，比如可对一个<strong><code>程序员</code></strong>在<strong><code>项目A</code></strong>、<strong><code>项目B</code></strong>、<strong><code>项目C</code></strong>的代码质量进行比较分析</p></blockquote><ul><li>报表<br>  <img src="KpiBatchReportRadar-03.png" alt=""></li><li>雷达图<br>  <img src="KpiBatchReportRadar-04.png" alt=""></li></ul><h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1><h2 id="指标计算公式中的变量类型"><a href="#指标计算公式中的变量类型" class="headerlink" title="指标计算公式中的变量类型"></a>指标计算公式中的变量类型</h2><h3 id="普通变量"><a href="#普通变量" class="headerlink" title="普通变量"></a>普通变量</h3><ul><li>说明：对单个指标的值进行引用</li><li>格式：<strong><code>${&lt;指标名&gt;}</code></strong></li><li>样例： <strong><code>${下单金额}</code></strong></li></ul><h3 id="通配符变量"><a href="#通配符变量" class="headerlink" title="通配符变量"></a>通配符变量</h3><ul><li>说明：通过<strong><code>通配符</code></strong>匹配指标名</li><li>格式：<strong><code>${xxx*xxx??xxx}</code></strong>，其中<strong><code>*</code></strong>代表任意多个字符，<strong><code>?</code></strong>代表任意单个字符</li><li>样例1：<strong><code>${*得分}</code></strong>代表以<strong><code>得分</code></strong>结尾的所有指标</li><li>样例2：<strong><code>${??金额}</code></strong>代表总共4字，以<strong><code>金额</code></strong>结尾的指标</li></ul><h3 id="正则表达式变量"><a href="#正则表达式变量" class="headerlink" title="正则表达式变量"></a>正则表达式变量</h3><ul><li>说明：通过<strong><code>正则表达式</code></strong>匹配指标名</li><li>格式：<strong><code>#{&lt;表达式&gt;}</code></strong></li><li>样例：<strong><code>#{0[1-6]月金额}</code></strong>匹配<strong><code>01月金额</code></strong>～<strong><code>06月金额</code></strong>六个指标</li><li>备注：需要具备正则表达式知识，可由技术人员协助配置。（<a href="https://baike.baidu.com/item/正则表达式" target="_blank" rel="noopener">正则表达式参考</a>）</li></ul><h2 id="部分DSL方法"><a href="#部分DSL方法" class="headerlink" title="部分DSL方法"></a>部分DSL方法</h2><blockquote><p>以下为当前实现的部份DSL方法，系统支持DSL方法的扩展，可根据不同业务领域的特殊要求进行定制</p></blockquote><h3 id="百分比汇总"><a href="#百分比汇总" class="headerlink" title="百分比汇总"></a>百分比汇总</h3><ul><li>功能说明：多个指标根据百分比加权求和</li><li>参数：一组包含值(value)和占比(percentage)的列表</li><li>返回：数值</li><li>样例  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 按百分比汇总</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;例如 指标A = 百分比汇总([[$&#123;指标1&#125;, 20], [$&#123;指标2&#125;, 80]])</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;表示 指标A由指标1和指标2，其中指标1占比20%，指标2占比80%</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;相当于 指标A = 指标1 * 20% + 指标2 * 80%</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;需注意：传入的参数列表中，所有的占比(percentage)相加需等于100，否则计算出错没有结果</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> valueList 参数1，一组包含值(value)和占比(percentage)的列表</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 汇总后的值</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="条件匹配"><a href="#条件匹配" class="headerlink" title="条件匹配"></a>条件匹配</h3><ul><li>功能说明：在匹配项列表找到匹配项，返回对应值</li><li>参数：（输入Map，匹配项列表）</li><li>返回：任意值</li><li>样例  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 根据输入项和条件进行匹配，计算结果</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> inputMap 输入项，如：v1:$&#123;金额&#125;, v2: $&#123;数量&#125;*</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> expList 匹配项列表，如：</span></span><br><span class="line"><span class="comment">    * [['v1 &lt;= 50', 40],</span></span><br><span class="line"><span class="comment">    * ['v1 &gt;  50 &amp;&amp; v1 &lt;= 150', 60],</span></span><br><span class="line"><span class="comment">    * ['v1 &gt; 150 &amp;&amp; v1 &lt;= 300', 80],</span></span><br><span class="line"><span class="comment">    * ['v1 &gt; 300 &amp;&amp; v2 &lt;=100', 90],</span></span><br><span class="line"><span class="comment">    * ['v1 &gt; 300 &amp;&amp; v2 &gt; 100', 100]]</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span> 匹配结果，如果都不匹配返回为空</span></span><br><span class="line"><span class="comment">    */</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="平均值"><a href="#平均值" class="headerlink" title="平均值"></a>平均值</h3><ul><li>功能说明：计算一组数值的平均值</li><li>参数：指标列表</li><li>返回：数值</li><li>样例：<strong><code>平均值([${*得分}])</code></strong>,代表以“得分”结尾的所有指标的平均值</li></ul><h3 id="最小值"><a href="#最小值" class="headerlink" title="最小值"></a>最小值</h3><ul><li>功能说明：查找一组数值的最小值</li><li>参数：指标列表</li><li>返回：数值</li><li>样例：<strong><code>最小值([${*得分}])</code></strong></li></ul><h3 id="最大值"><a href="#最大值" class="headerlink" title="最大值"></a>最大值</h3><ul><li>功能说明：查找一组数值的最大值</li><li>参数：指标列表</li><li>返回：数值</li><li>样例：<strong><code>最大值([${*得分}])</code></strong></li></ul><h3 id="总和"><a href="#总和" class="headerlink" title="总和"></a>总和</h3><ul><li>功能说明：计算一组数值的和</li><li>参数：指标列表</li><li>返回：数值</li><li>样例：<strong><code>总和([${*得分}])</code></strong></li></ul><h3 id="标准差"><a href="#标准差" class="headerlink" title="标准差"></a>标准差</h3><ul><li>功能说明：计算一组数值的标准差</li><li>参数：指标列表</li><li>返回：数值</li><li>样例：<strong><code>标准差([${指标1},${指标2},${下单??}])</code></strong></li></ul><h3 id="匹配左闭区间"><a href="#匹配左闭区间" class="headerlink" title="匹配左闭区间"></a>匹配左闭区间</h3><ul><li>功能说明：找到传入值对应的区间（区间类型为右开左闭），返回对应值</li><li>参数：匹配值，匹配区间列表</li><li>返回：任意值</li><li>样例  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">匹配左闭区间($&#123;年龄&#125;,</span><br><span class="line">    [[<span class="keyword">null</span>, <span class="number">20</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">20</span>, <span class="number">40</span>, <span class="number">80</span>],</span><br><span class="line">    [<span class="number">40</span>, <span class="number">60</span>, <span class="number">100</span>],</span><br><span class="line">    [<span class="number">60</span>, <span class="keyword">null</span>, <span class="number">60</span>]])</span><br></pre></td></tr></table></figure></li></ul><h3 id="匹配右闭区间"><a href="#匹配右闭区间" class="headerlink" title="匹配右闭区间"></a>匹配右闭区间</h3><ul><li>功能说明：找到传入值对应的区间（区间类型为右闭左开），返回对应值</li><li>参数：匹配值，匹配区间列表</li><li>返回：任意值</li><li>样例：参考<strong><code>匹配左闭区间</code></strong></li></ul><h3 id="匹配闭区间"><a href="#匹配闭区间" class="headerlink" title="匹配闭区间"></a>匹配闭区间</h3><ul><li>功能说明：找到传入值对应的区间（区间类型为右左都闭），返回对应值</li><li>参数：匹配值，匹配区间列表</li><li>返回：任意值</li><li>样例：参考<strong><code>匹配左闭区间</code></strong></li></ul><h3 id="匹配开区间"><a href="#匹配开区间" class="headerlink" title="匹配开区间"></a>匹配开区间</h3><ul><li>功能说明：找到传入值对应的区间（区间类型为右左都开），返回对应值</li><li>参数：匹配值，匹配区间列表</li><li>返回：任意值</li><li>样例：参考<strong><code>匹配左闭区间</code></strong></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;指标模型的概念&quot;&gt;&lt;a href=&quot;#指标模型的概念&quot; class=&quot;headerlink&quot; title=&quot;指标模型的概念&quot;&gt;&lt;/a&gt;指标模型的概念&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;关键指标分析是一种常用的业务分析手段&lt;/li&gt;
&lt;li&gt;指标可分为&lt;strong&gt;&lt;co
      
    
    </summary>
    
    
      <category term="产品介绍" scheme="https://neoscript99.github.io/tags/%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D/"/>
    
  </entry>
  
  <entry>
    <title>Deepin-Ubuntu-Linux学习笔记</title>
    <link href="https://neoscript99.github.io/2017/12/12/2017/deepin-ubuntu-linux-learn/"/>
    <id>https://neoscript99.github.io/2017/12/12/2017/deepin-ubuntu-linux-learn/</id>
    <published>2017-12-12T05:54:29.000Z</published>
    <updated>2019-01-15T09:15:50.593Z</updated>
    
    <content type="html"><![CDATA[<h2 id="软件安装"><a href="#软件安装" class="headerlink" title="软件安装"></a>软件安装</h2><h3 id="阿里云镜像"><a href="#阿里云镜像" class="headerlink" title="阿里云镜像"></a>阿里云镜像</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#备份</span></span><br><span class="line">sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak</span><br><span class="line"><span class="comment">#替换</span></span><br><span class="line">sudo sed -ni <span class="string">'s/packages.deepin.com\/deepin/mirrors.aliyun.com\/deepin\//p'</span> /etc/apt/sources.list</span><br></pre></td></tr></table></figure><h3 id="命令行工具"><a href="#命令行工具" class="headerlink" title="命令行工具"></a>命令行工具</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#Tilix终端</span></span><br><span class="line"><span class="comment">##install</span></span><br><span class="line">sudo apt install tilix -y</span><br><span class="line"><span class="comment">##start with root</span></span><br><span class="line">gksudo tilix &amp;</span><br><span class="line"></span><br><span class="line"><span class="comment">#git</span></span><br><span class="line">sudo apt install git -y</span><br><span class="line"></span><br><span class="line"><span class="comment">#oh-my-zsh</span></span><br><span class="line"><span class="comment">##zsh first</span></span><br><span class="line">sudo apt install zsh -y</span><br><span class="line"><span class="comment">##via curl</span></span><br><span class="line">sudo apt install curl -y</span><br><span class="line">sh -c <span class="string">"<span class="variable">$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)</span>"</span></span><br><span class="line"><span class="comment">##via wget</span></span><br><span class="line">sh -c <span class="string">"<span class="variable">$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#autojump</span></span><br><span class="line">sudo apt install autojump -y</span><br><span class="line"><span class="built_in">echo</span> <span class="string">". /usr/share/autojump/autojump.sh"</span> &gt;&gt; ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="comment">#ssh密码工具</span></span><br><span class="line">sudo apt install sshpass -y</span><br></pre></td></tr></table></figure><h3 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#idea</span></span><br><span class="line">sudo apt install idea -y</span><br><span class="line"></span><br><span class="line"><span class="comment">#vscode</span></span><br><span class="line">sudo apt install vscode -y</span><br><span class="line"></span><br><span class="line"><span class="comment">#meld</span></span><br><span class="line">sudo apt install meld -y</span><br><span class="line"></span><br><span class="line"><span class="comment">#nvm</span></span><br><span class="line"><span class="comment">##To install or update nvm, you can use the install script using cURL:</span></span><br><span class="line">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash</span><br><span class="line"><span class="comment">##or Wget:</span></span><br><span class="line">wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash</span><br><span class="line"><span class="comment">##The script clones the nvm repository to ~/.nvm and adds the source line to your profile (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc).</span></span><br><span class="line"><span class="built_in">export</span> NVM_DIR=<span class="string">"<span class="variable">$HOME</span>/.nvm"</span></span><br><span class="line">[ -s <span class="string">"<span class="variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; . <span class="string">"<span class="variable">$NVM_DIR</span>/nvm.sh"</span> <span class="comment"># This loads nvm</span></span><br><span class="line"></span><br><span class="line">nvm install stable</span><br><span class="line"><span class="comment">##淘宝镜像配置</span></span><br><span class="line">npm config <span class="built_in">set</span> registry https://registry.npm.taobao.org</span><br><span class="line"></span><br><span class="line"><span class="comment">#bash 格式化工具</span></span><br><span class="line"><span class="comment">##amazonaws，openconnect first</span></span><br><span class="line"><span class="built_in">cd</span> /usr/<span class="built_in">local</span>/bin</span><br><span class="line">sudo wget https://github.com/mvdan/sh/releases/download/v2.2.0/shfmt_v2.2.0_linux_amd64 -O shfmt</span><br><span class="line">sudo chmod +x shfmt</span><br><span class="line"></span><br><span class="line"><span class="comment">#jd-gui </span></span><br><span class="line"><span class="comment">##amazonaws，openconnect first</span></span><br><span class="line"><span class="built_in">cd</span> ~/Desktop</span><br><span class="line">wget https://github.com/java-decompiler/jd-gui/releases/download/v1.4.0/jd-gui_1.4.0-0_all.deb</span><br><span class="line">sudo dpkg -i jd-gui_1.4.0-0_all.deb</span><br></pre></td></tr></table></figure><h3 id="其他软件"><a href="#其他软件" class="headerlink" title="其他软件"></a>其他软件</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#locate</span></span><br><span class="line">sudo apt install locate -y</span><br><span class="line">sudo mv /etc/cron.daily/locate /etc/cron.hourly</span><br><span class="line"></span><br><span class="line"><span class="comment">#微信wine版</span></span><br><span class="line">sudo apt install deepin.com.wechat -y</span><br><span class="line"></span><br><span class="line"><span class="comment">#openconnect</span></span><br><span class="line"><span class="comment">##Connect to Cisco AnyConnect VPN</span></span><br><span class="line">sudo apt install openconnect -y</span><br><span class="line">openconnect -c xxx.p12 -p xxpassword --servercert sha256:bbdbxxx...xxx31260 vpn.xxx.com</span><br><span class="line"></span><br><span class="line"><span class="comment">#Chrome</span></span><br><span class="line">sudo apt install google-chrome-stable -y</span><br></pre></td></tr></table></figure><h2 id="配置文件备份"><a href="#配置文件备份" class="headerlink" title="配置文件备份"></a>配置文件备份</h2><blockquote><p>可通过硬链接的方式，将以下配置文件映射到git仓库进行版本管理<br>比如：<code>ln /xxx/vscode_settings.json ~/.config/Code/User/settings.json</code></p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#vscode</span></span><br><span class="line">~/.config/Code/User/settings.json</span><br></pre></td></tr></table></figure><h2 id="管理员运行"><a href="#管理员运行" class="headerlink" title="管理员运行"></a>管理员运行</h2><ul><li>For a console program use: <code>sudo &lt;program name&gt;</code></li><li><p>If it is a GUI application use: <code>gksudo &lt;program name&gt;</code></p></li><li><p>可修改<code>/usr/share/applications</code>和<code>/usr/local/share/applications</code>的快捷方式</p></li></ul><h2 id="启动脚本"><a href="#启动脚本" class="headerlink" title="启动脚本"></a>启动脚本</h2><h3 id="系统级"><a href="#系统级" class="headerlink" title="系统级"></a>系统级</h3><ol><li>/etc/environment</li><li>/etc/xprofile bash script executed while starting X Window System session. </li><li>/etc/profile and /etc/profile.d/*</li><li><code>/etc/&lt;bash&gt;.&lt;bash&gt;rc</code> 作用单独bash script. This is a poor choice because it is single bash specific.</li></ol><h3 id="用户级"><a href="#用户级" class="headerlink" title="用户级"></a>用户级</h3><p>~/.pam_environment ~/.xprofile ~/.profile <code>~/.&lt;bash&gt;rc</code></p><h2 id="GRUB"><a href="#GRUB" class="headerlink" title="GRUB"></a>GRUB</h2><h3 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#/etc/default/grub 优先 /usr/share/grub/default/grub</span></span><br><span class="line">sudo vi /etc/default/grub</span><br><span class="line">sudo update-grub</span><br><span class="line"><span class="comment">#确认</span></span><br><span class="line">grep timeout /boot/grub/grub.cfg</span><br></pre></td></tr></table></figure><h3 id="启动-菜单无法显示问题"><a href="#启动-菜单无法显示问题" class="headerlink" title="启动 菜单无法显示问题"></a><a href="https://community.linuxmint.com/tutorial/view/842" target="_blank" rel="noopener">启动 菜单无法显示问题</a></h3><h2 id="SSH免密-sshpass"><a href="#SSH免密-sshpass" class="headerlink" title="SSH免密 - sshpass"></a>SSH免密 - sshpass</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> SSHPASS=<span class="variable">$DEPLOY_PASS</span></span><br><span class="line">sshpass -e scp package.tgz <span class="variable">$DEPLOY_USER</span>@<span class="variable">$DEPLOY_HOST</span>:<span class="variable">$DEPLOY_PATH</span></span><br><span class="line">sshpass -e ssh <span class="variable">$DEPLOY_USER</span>@<span class="variable">$DEPLOY_HOST</span> <span class="variable">$DEPLOY_PATH</span>/deploy.sh</span><br></pre></td></tr></table></figure><h2 id="SSH保持连接配置"><a href="#SSH保持连接配置" class="headerlink" title="SSH保持连接配置"></a>SSH保持连接配置</h2><h3 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#/etc/ssh/ssh_config</span></span><br><span class="line"></span><br><span class="line">ServerAliveInterval 60</span><br><span class="line">ServerAliveCountMax 3</span><br></pre></td></tr></table></figure><h3 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#/etc/ssh/sshd_config</span></span><br><span class="line"></span><br><span class="line">ClientAliveInterval 60</span><br><span class="line">ClientAliveCountMax 3</span><br></pre></td></tr></table></figure><h2 id="问题列表"><a href="#问题列表" class="headerlink" title="问题列表"></a>问题列表</h2><h3 id="Deepin没有声音"><a href="#Deepin没有声音" class="headerlink" title="Deepin没有声音"></a>Deepin没有声音</h3><blockquote><p>原因是deepin不会自动选择输入输出设备，解决方法如下：</p></blockquote><ol><li>点击任务栏上的“控制中心”</li><li>在“按制中心”中单击“声音”</li><li>点击“声音”后在弹出的界面中单击“高级设置”</li><li>在“高级设置”中单击将输出设备修改为“模拟耳机”</li><li>除此之外，如果安装了pavucontrl，这是pulse audio服务的一个控制程序， 也可以进行类似的设置。</li></ol><h3 id="FlashPlayer-Linux-编译问题"><a href="#FlashPlayer-Linux-编译问题" class="headerlink" title="FlashPlayer Linux 编译问题"></a>FlashPlayer Linux 编译问题</h3><ul><li><p>错误信息</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Error:[NSFlexLib]: Picked up _JAVA_OPTIONS:   -Dawt.useSystemAAFontSettings=gasp</span><br><span class="line">Error:[NSFlexLib]: Compiler process is not started.</span><br></pre></td></tr></table></figure></li><li><p>原因、解决办法：deepin的jdk默认加入了<code>-Dawt.useSystemAAFontSettings=gasp</code>（/etc/profile.d/java-awt-font-gasp.sh），需去除</p>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo gzip /etc/profile.d/java-awt-font-gasp.sh</span><br></pre></td></tr></table></figure></li></ul><h3 id="FlashPlayer-Linux-Debug问题"><a href="#FlashPlayer-Linux-Debug问题" class="headerlink" title="FlashPlayer Linux Debug问题"></a>FlashPlayer Linux Debug问题</h3><ul><li><p>错误信息</p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">java.lang.NullPointerException</span><br><span class="line">at flash.tools.debugger.concrete.PlayerSession.pullUpActivationObjectVariables(PlayerSession.java:<span class="number">1007</span>)</span><br><span class="line">at flash.tools.debugger.concrete.PlayerSession.requestFrame(PlayerSession.java:<span class="number">984</span>)</span><br><span class="line">at flash.tools.debugger.concrete.DStackContext.populate(DStackContext.java:<span class="number">156</span>)</span><br><span class="line">at flash.tools.debugger.concrete.DStackContext.getArguments(DStackContext.java:<span class="number">74</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.appendFrameInfo(DebugCLI.java:<span class="number">1202</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.doInfoStack(DebugCLI.java:<span class="number">1167</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.processLine(DebugCLI.java:<span class="number">6471</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.process(DebugCLI.java:<span class="number">727</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.execute(DebugCLI.java:<span class="number">569</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.main(DebugCLI.java:<span class="number">374</span>)</span><br><span class="line">java.lang.NullPointerException</span><br><span class="line">at flash.tools.debugger.concrete.PlayerSession.pullUpActivationObjectVariables(PlayerSession.java:<span class="number">1007</span>)</span><br><span class="line">at flash.tools.debugger.concrete.PlayerSession.requestFrame(PlayerSession.java:<span class="number">984</span>)</span><br><span class="line">at flash.tools.debugger.concrete.DStackContext.populate(DStackContext.java:<span class="number">156</span>)</span><br><span class="line">at flash.tools.debugger.concrete.DStackContext.getArguments(DStackContext.java:<span class="number">74</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.appendFrameInfo(DebugCLI.java:<span class="number">1202</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.doInfoStack(DebugCLI.java:<span class="number">1167</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.processLine(DebugCLI.java:<span class="number">6471</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.process(DebugCLI.java:<span class="number">727</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.execute(DebugCLI.java:<span class="number">569</span>)</span><br><span class="line">at flex.tools.debugger.cli.DebugCLI.main(DebugCLI.java:<span class="number">374</span>)</span><br></pre></td></tr></table></figure></li><li><p>原因分析：出错时选中的debug sdk是工程sdk，是3.6，可能老的sdk和新的linux flashplayer不兼容</p></li><li>解决办法：在idea选中新版本的sdk做debug，这个sdk可以跟编译版本的sdk不同<blockquote><p>在<strong>Run/Debug Configuration</strong>页面，配置<strong>“Use debugger from SDK”</strong>为<strong>4.x</strong><br>  <img src="flex_mojos_sdk.npg" alt=""></p></blockquote></li><li><a href="https://bugs.interactive-pioneers.de/browse/FDT-2999" target="_blank" rel="noopener">讨论网址</a><blockquote><p>The Flex SDK Debugger Adapter of the Flex SDK 3.6 has some bugs especially with 64-bit platforms<br>(also Ubuntu 64-bit, times back when Adobe was also supporting Linux)<br>This was the reason for us to allow the user to choose the Flex SDK to debug with.</p></blockquote></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;软件安装&quot;&gt;&lt;a href=&quot;#软件安装&quot; class=&quot;headerlink&quot; title=&quot;软件安装&quot;&gt;&lt;/a&gt;软件安装&lt;/h2&gt;&lt;h3 id=&quot;阿里云镜像&quot;&gt;&lt;a href=&quot;#阿里云镜像&quot; class=&quot;headerlink&quot; title=&quot;阿里云镜像&quot;&gt;
      
    
    </summary>
    
    
      <category term="linux" scheme="https://neoscript99.github.io/tags/linux/"/>
    
      <category term="学习笔记" scheme="https://neoscript99.github.io/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>Spock测试框架学习笔记</title>
    <link href="https://neoscript99.github.io/2017/08/24/2017/spock-test-learn/"/>
    <id>https://neoscript99.github.io/2017/08/24/2017/spock-test-learn/</id>
    <published>2017-08-23T16:00:00.000Z</published>
    <updated>2019-01-15T09:15:50.593Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Spock操作符重载"><a href="#Spock操作符重载" class="headerlink" title="Spock操作符重载"></a>Spock操作符重载</h1><h2 id="参数化测试"><a href="#参数化测试" class="headerlink" title="参数化测试"></a>参数化测试</h2><ul><li><p>| , 分隔多个输入或输出</p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">false</span> | <span class="keyword">false</span> | -<span class="number">1</span></span><br></pre></td></tr></table></figure></li><li><p>|| , 分隔输入和输出</p></li><li>_ , 无返回结果  <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">where:</span> <span class="string">"sample image names are"</span></span><br><span class="line">pictureFile ||</span><br><span class="line"><span class="string">"screenshot.bmp"</span> || _</span><br><span class="line"><span class="string">"IMG3434.raw"</span> || _</span><br></pre></td></tr></table></figure></li></ul><h2 id="STUB"><a href="#STUB" class="headerlink" title="STUB"></a>STUB</h2><ul><li><p>>>> , 添加多个返回结果，按照集合顺序返回</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">reader.getCurrentReadings() &gt;&gt;&gt; [prev,current]</span><br><span class="line">inventory.isProductAvailable( &quot;bravia&quot;, _) &gt;&gt;&gt; true &gt;&gt; false</span><br></pre></td></tr></table></figure></li><li><p>>> , 添加单个返回结果，可以用闭包处理或抛异常</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">isProductAvailable(&quot;bravia&quot;,1) &gt;&gt; true</span><br><span class="line">inventory.isProductAvailable( &quot;bravia&quot;, _) &gt;&gt; &#123;</span><br><span class="line">    throw new RuntimeException(&quot;critical error&quot;)</span><br><span class="line">&#125;</span><br><span class="line">shippingCalculator.findShippingCostFor( _, _) &gt;&gt; &#123;</span><br><span class="line">    Product product, int count -&gt; 10 * count</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>_ , 代表任何变量</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">inventory.isProductAvailable(_, 1) &gt;&gt; true</span><br><span class="line">inventory.isProductAvailable( _, _) &gt;&gt; true</span><br></pre></td></tr></table></figure></li></ul><h2 id="MOCK"><a href="#MOCK" class="headerlink" title="MOCK"></a>MOCK</h2><ul><li><p>* , mock方法调用次数判断</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">1 * control.activateAlarm()</span><br><span class="line">1 * creditCardSevice.sale(1200,customer)</span><br><span class="line">2 * inventory.isProductAvailable( _ , _) &gt;&gt; true</span><br><span class="line">0 * inventory._  //(no call for other method)</span><br><span class="line">2 * inventory.isProductAvailable(!null ,1) &gt;&gt; true</span><br><span class="line">2 * inventory.isProductAvailable(_ as String ,_ as Integer) &gt;&gt; true</span><br><span class="line">1 * creditCardSevice.sale(1550, &#123; client -&gt; client.vip == false&#125;)</span><br><span class="line">1 * creditCardSevice.sale(</span><br><span class="line">    &#123;amount -&gt; amount ==basket.findOrderPrice()&#125;,</span><br><span class="line">    &#123; client -&gt; client.vip == false&#125;)</span><br></pre></td></tr></table></figure></li><li><p>_ , 代表任何变量、方法、类</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">2 * inventory.isProductAvailable( _ , _) &gt;&gt; true</span><br><span class="line">_ * inventory.isEmpty() &gt;&gt; false</span><br><span class="line">0 * _</span><br></pre></td></tr></table></figure></li></ul><h1 id="Behavior-Testing-Paradigm"><a href="#Behavior-Testing-Paradigm" class="headerlink" title="Behavior-Testing Paradigm"></a>Behavior-Testing Paradigm</h1><p><code>Available Spock blocks</code></p><p> <code>(given-when-then )( 相当于setup-stimulate-assert structure of JUnit )</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">() &#123;</span><br><span class="line">   given: FireEarlyWarning fireEarlyWarning =FireEarlyWarning()</span><br><span class="line">   triggeredSensors = when: fireEarlyWarning.feedData(triggeredSensors)</span><br><span class="line">   WarningStatus status = fireEarlyWarning.()</span><br><span class="line"></span><br><span class="line">   then: !status.!status.&#125;</span><br><span class="line"></span><br><span class="line">() &#123;</span><br><span class="line">   given: FireEarlyWarning fireEarlyWarning =FireEarlyWarning()</span><br><span class="line">   triggeredSensors = when: fireEarlyWarning.feedData(triggeredSensors)</span><br><span class="line">   WarningStatus status = fireEarlyWarning.()</span><br><span class="line"></span><br><span class="line">   then: status.!status.&#125;</span><br></pre></td></tr></table></figure><h1 id="Parameterized-Test"><a href="#Parameterized-Test" class="headerlink" title="Parameterized Test"></a>Parameterized Test</h1><blockquote><p>相当于JUint的 @RunWith(Parameterized.class)</p></blockquote><ol><li><p>Using data tables in the where: block</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="string">"Complete test of all nuclear scenarios"</span>() &#123;</span><br><span class="line"><span class="symbol">    given:</span> <span class="string">"a nuclear reactor and sensor data"</span></span><br><span class="line">    NuclearReactorMonitor nuclearReactorMonitor =<span class="keyword">new</span> NuclearReactorMonitor()</span><br><span class="line"></span><br><span class="line"><span class="symbol">    when:</span> <span class="string">"we examine the sensor data"</span></span><br><span class="line">    nuclearReactorMonitor.feedFireSensorData(fireSensors)</span><br><span class="line">    nuclearReactorMonitor.feedRadiationSensorData(radiation)</span><br><span class="line">    nuclearReactorMonitor.feedPressureInBar(pressure)</span><br><span class="line">    NuclearReactorStatus status = nuclearReactorMonitor.getCurrentStatus()</span><br><span class="line"></span><br><span class="line"><span class="symbol">    then:</span> <span class="string">"we act according to safety requirements"</span></span><br><span class="line">    status.alarmActive == alarm</span><br><span class="line">    status.shutDownNeeded == shutDown</span><br><span class="line">    status.evacuationMinutes == evacuation</span><br><span class="line"></span><br><span class="line"><span class="symbol">    where:</span> <span class="string">"possible nuclear incidents are:"</span></span><br><span class="line">    pressure | fireSensors | radiation || alarm | shutDown | evacuation</span><br><span class="line">    <span class="number">150</span> | <span class="number">0</span> | [] || <span class="literal">false</span> | <span class="literal">false</span> | <span class="number">-1</span></span><br><span class="line">    <span class="number">150</span> | <span class="number">1</span> | [] || <span class="literal">true</span> | <span class="literal">false</span> | <span class="number">-1</span></span><br><span class="line">    <span class="number">150</span> | <span class="number">3</span> | [] || <span class="literal">true</span> | <span class="literal">true</span> | <span class="number">-1</span></span><br><span class="line">    <span class="number">150</span> | <span class="number">0</span>| [<span class="number">110.4</span>f ,<span class="number">0.3</span>f, <span class="number">0.0</span>f] || <span class="literal">true</span> | <span class="literal">true</span> | <span class="number">1</span></span><br><span class="line">    <span class="number">150</span> | <span class="number">0</span>| [<span class="number">45.3</span>f ,<span class="number">10.3</span>f, <span class="number">47.7</span>f]|| <span class="literal">false</span> | <span class="literal">false</span> | <span class="number">-1</span></span><br><span class="line">    <span class="number">155</span> | <span class="number">0</span>| [<span class="number">0.0</span>f ,<span class="number">0.0</span>f, <span class="number">0.0</span>f] || <span class="literal">true</span> | <span class="literal">false</span> | <span class="number">-1</span></span><br><span class="line">    <span class="number">170</span> | <span class="number">0</span>| [<span class="number">0.0</span>f ,<span class="number">0.0</span>f, <span class="number">0.0</span>f] || <span class="literal">true</span> | <span class="literal">true</span> | <span class="number">3</span></span><br></pre></td></tr></table></figure></li><li><p>Using data pipes for calculating input/output parameters</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">() &#123;</span><br><span class="line"><span class="symbol">   given:</span> ImageNameValidator validator = ImageNameValidator() </span><br><span class="line"></span><br><span class="line"><span class="symbol">   expect:</span> validator.isValidImageExtension(pictureFile) == validPicture </span><br><span class="line"></span><br><span class="line"><span class="symbol">   where:</span> pictureFile  &lt;&lt; [,, ,,]</span><br><span class="line">   validPicture &lt;&lt; [ , , , , ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>Dynamically generated parameters</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">() &#123;</span><br><span class="line"><span class="symbol">   given:</span> Calculator calc = Calculator()</span><br><span class="line"></span><br><span class="line"><span class="symbol">   expect:</span> calc.multiply(first,second) &lt; <span class="string">where:</span> first &lt;&lt; (..)</span><br><span class="line">   second &lt;&lt; (-..-)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>Parameters that stay constant</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">where:</span> <span class="string">"some scenarios are"</span></span><br><span class="line">first &lt;&lt; [<span class="number">20</span>,<span class="number">34</span>,<span class="number">44</span>,<span class="number">67</span>]</span><br><span class="line">second = <span class="number">-1</span></span><br></pre></td></tr></table></figure></li><li><p>Parameters that depend on other parameters </p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">where:</span> <span class="string">"some scenarios are"</span></span><br><span class="line">first &lt;&lt; [<span class="number">20</span>,<span class="number">34</span>,<span class="number">44</span>,<span class="number">67</span>]</span><br><span class="line">second = first * <span class="number">-1</span></span><br></pre></td></tr></table></figure></li><li><p>Using dedicated data generators</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pictureFile &lt;&lt; <span class="keyword">new</span> File(<span class="string">"src/test/resources/validImageNames.txt"</span>).readLines()</span><br></pre></td></tr></table></figure></li><li><p>Writing a custom data generator</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InvalidNamesGen</span> <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">String</span>&gt;&#123;</span> ...&#125;</span><br><span class="line"><span class="string">where:</span> <span class="string">"sample image names are"</span></span><br><span class="line">pictureFile &lt;&lt; <span class="keyword">new</span> InvalidNamesGen()</span><br></pre></td></tr></table></figure></li><li><p>Using multivalued data iterators</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">() &#123;</span><br><span class="line"><span class="symbol">   given:</span> ImageNameValidator validator = ImageNameValidator()</span><br><span class="line">   </span><br><span class="line">   </span><br><span class="line"><span class="symbol">   expect:</span> validator.isValidImageExtension(pictureFile) == result</span><br><span class="line">   </span><br><span class="line"><span class="symbol">   where:</span> [pictureFile,result] &lt;&lt; MultiVarReader()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>Working with third-party data generators<br>! <a href="https://github.com/Bijnagte/spock-genesis" target="_blank" rel="noopener">Spock genesis library</a> </p></li></ol><h1 id="Mocking-amp-Stubing"><a href="#Mocking-amp-Stubing" class="headerlink" title="Mocking&amp;Stubing"></a>Mocking&amp;Stubing</h1><ol><li>A stub is a fake class that comes with preprogrammed return values. It’s injected in the class under test so that you have absolute control of what’s being tested as input.</li><li>A mock is a fake11 class that can be examined after the test is finished for its interactions with the class under test (for example, you can ask it whether a method was called or how many times it was called).</li><li><code>Spock only records the interactions of mocks in the when: block (which should always contain the trigger code)</code></li><li><p>stub 多次调用，返回结果内容、顺序控制</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">reader.() &gt;&gt;&gt; [prev,current]</span><br><span class="line">TemperatureMonitor monitor = TemperatureMonitor(reader)</span><br><span class="line"><span class="string">when:</span> monitor.readSensor()</span><br><span class="line">monitor.readSensor()</span><br></pre></td></tr></table></figure></li><li><p>mock语法</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ReactorControl control = Mock(ReactorControl)</span><br><span class="line">then: <span class="string">"everything should be ok"</span> * control.shutdownReactor()</span><br><span class="line">* control.activateAlarm()</span><br></pre></td></tr></table></figure></li><li><p>Combining mocks and stubs in parameterized tests</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">then:</span> shutDown * control.shutdownReactor()</span><br><span class="line">alarm * control.activateAlarm()</span><br><span class="line"><span class="string">where:</span> </span><br><span class="line">previousTemp | currentTemp       ||  alarm | shutDown</span><br></pre></td></tr></table></figure></li><li><p>Spy 针对设计不良的老代码，需要做部分mock，其它部分还是运行实际代码。如果能做refactor，尽量避免使用Spy</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">and:</span> <span class="string">"the auto-nuker program"</span></span><br><span class="line">SmartHardDriveNuker nuker = Spy(SmartHardDriveNuker)</span><br><span class="line"><span class="comment">//只mock deleteHardDriveNow()方法</span></span><br><span class="line">nuker.deleteHardDriveNow() &gt;&gt; &#123;println <span class="string">"Hard disk is cleared"</span>&#125;</span><br></pre></td></tr></table></figure></li><li><p>动态方法重载</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="string">'test save map'</span>() &#123;</span><br><span class="line"><span class="symbol">    given:</span></span><br><span class="line">    GeneralRepository generalRepository = Mock(GeneralRepository)</span><br><span class="line">    <span class="comment">//1</span></span><br><span class="line">    generalRepository.saveEntity(_) &gt;&gt; &#123; Kpi kpi -&gt;</span><br><span class="line">        println kpi</span><br><span class="line">        <span class="keyword">return</span> kpi</span><br><span class="line">    &#125;</span><br><span class="line">    KpiService kpiService = <span class="keyword">new</span> KpiService(<span class="string">generalRepository:</span> generalRepository)</span><br><span class="line"></span><br><span class="line"><span class="symbol">    when:</span></span><br><span class="line">    Kpi kpi = kpiService.save([<span class="string">kpiCode:</span><span class="string">"A01"</span>])</span><br><span class="line"></span><br><span class="line"><span class="symbol">    then:</span></span><br><span class="line">    <span class="comment">//2</span></span><br><span class="line">    <span class="number">1</span> * generalRepository.saveEntity(_)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p><strong>1和2冲突，有2的话1不起作用</strong></p><h1 id="Spock注解"><a href="#Spock注解" class="headerlink" title="Spock注解"></a>Spock注解</h1><ol><li><p>@Subject（Marking the class under test inside a Spock test )</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">given:</span> <span class="string">"an empty basket"</span></span><br><span class="line"><span class="meta">@Subject</span></span><br><span class="line">Basket basket = <span class="keyword">new</span> Basket()</span><br></pre></td></tr></table></figure><p> <code>The subject of this test is the Basket class.</code></p></li><li><p>@Title(Describing the Spock unit test as a whole )</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Title</span>(<span class="string">"Unit test for basket weight"</span>)</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BasketWeightSpec</span> <span class="keyword">extends</span> <span class="title">spock</span>.<span class="title">lang</span>.<span class="title">Specification</span>&#123;</span></span><br><span class="line">    [...test methods here redacted <span class="keyword">for</span> brevity...]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>@Narrative (详述)</p> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Narrative</span>(<span class="string">""" A empty basket starts with no weight. Adding products to the basket increases its weight. The weight is</span></span><br><span class="line"><span class="string">then used for billing during shipping calculations. Electronic goods have always zero weight."""</span>)</span><br><span class="line"><span class="meta">@Title</span>(<span class="string">"Unit test for basket weight"</span>)</span><br><span class="line"><span class="meta">@Subject</span>(Basket)</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BasketWeightDetailedSpec</span> <span class="keyword">extends</span> <span class="title">spock</span>.<span class="title">lang</span>.<span class="title">Specification</span>&#123;</span></span><br><span class="line">[...test methods here redacted <span class="keyword">for</span> brevity...]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>@Shared (Long-lived objects )</p></li><li><p>@Unroll( annotation for reporting individual test runs )</p><blockquote><p>自动分割测试案例，并通过模板维护测试名称</p></blockquote> <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="string">"Trivial adder test"</span>() &#123;</span><br><span class="line"><span class="symbol">    given:</span> <span class="string">"an adder"</span></span><br><span class="line">    Adder adder = <span class="keyword">new</span> Adder()</span><br><span class="line"></span><br><span class="line"><span class="symbol">    when:</span> <span class="string">"the add method is called for two numbers"</span></span><br><span class="line">    <span class="keyword">int</span> result = adder.add(first,second)</span><br><span class="line"></span><br><span class="line"><span class="symbol">    then:</span> <span class="string">"the result should be the sum of them"</span></span><br><span class="line">    result ==sum</span><br><span class="line"></span><br><span class="line"><span class="symbol">    where:</span> <span class="string">"some scenarios are"</span></span><br><span class="line">    first |second || sum</span><br><span class="line">    <span class="number">1</span> | <span class="number">1</span> || <span class="number">2</span></span><br><span class="line">    <span class="number">3</span> | <span class="number">2</span> || <span class="number">5</span></span><br><span class="line">    <span class="number">3</span> | <span class="number">-3</span> || <span class="number">0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>@Stepwise ( 根据测试方法顺序执行 )</p></li><li>@Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) 设定超时时间</li><li>@AutoCleanup(“shutdown”) 自动清理，默认调用close方法</li></ol><h1 id="其它功能"><a href="#其它功能" class="headerlink" title="其它功能"></a>其它功能</h1><ol><li><p>setupSpec setup cleanup cleanupSpec</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LifecycleSpec</span> <span class="keyword">extends</span> <span class="title">spock</span>.<span class="title">lang</span>.<span class="title">Specification</span></span>&#123;</span><br><span class="line">    <span class="function">def <span class="title">setupSpec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="comment">//      println "Will run only once"</span></span><br><span class="line">    &#125;       </span><br><span class="line">    <span class="function">def <span class="title">setup</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="comment">//      println "Will run before EACH feature"</span></span><br><span class="line">    &#125;           </span><br><span class="line">    def <span class="string">"first feature being tested"</span>() &#123;</span><br><span class="line">        expect: <span class="string">"trivial test"</span></span><br><span class="line"><span class="comment">//      println "first feature runs"</span></span><br><span class="line">        <span class="number">2</span> == <span class="number">1</span> +<span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">    def <span class="string">"second feature being tested"</span>() &#123;</span><br><span class="line">        expect: <span class="string">"trivial test"</span></span><br><span class="line"><span class="comment">//      println "second feature runs"</span></span><br><span class="line">        <span class="number">5</span> == <span class="number">3</span> +<span class="number">2</span></span><br><span class="line">    &#125;   </span><br><span class="line">    <span class="function">def <span class="title">cleanup</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="comment">//      println "Will run once after EACH feature"</span></span><br><span class="line">    &#125;        </span><br><span class="line">    </span><br><span class="line">    <span class="function">def <span class="title">cleanupSpec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="comment">//      println "Will run once at the end"</span></span><br><span class="line">    &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>old()方法</p><blockquote><p>get value in given: block,before the when: block</p></blockquote></li><li>Hamcrest matchers  <figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">expect:</span> <span class="string">"camera should be one of them"</span></span><br><span class="line">products hasItem(<span class="string">"camera"</span>)</span><br><span class="line"><span class="string">and:</span> <span class="string">"hotdog is not one of them"</span></span><br><span class="line">products not(hasItem(<span class="string">"hotdog"</span>))</span><br></pre></td></tr></table></figure></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Spock操作符重载&quot;&gt;&lt;a href=&quot;#Spock操作符重载&quot; class=&quot;headerlink&quot; title=&quot;Spock操作符重载&quot;&gt;&lt;/a&gt;Spock操作符重载&lt;/h1&gt;&lt;h2 id=&quot;参数化测试&quot;&gt;&lt;a href=&quot;#参数化测试&quot; class=&quot;he
      
    
    </summary>
    
    
      <category term="学习笔记" scheme="https://neoscript99.github.io/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
      <category term="groovy" scheme="https://neoscript99.github.io/tags/groovy/"/>
    
      <category term="junit" scheme="https://neoscript99.github.io/tags/junit/"/>
    
      <category term="测试" scheme="https://neoscript99.github.io/tags/%E6%B5%8B%E8%AF%95/"/>
    
  </entry>
  
</feed>
