<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>cyri_ (Cyril Kato)</title>
    <link>https://ruby-china.org/cyri_</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Fix 1.0：先写规范，再测试</title>
      <description>&lt;p&gt;Fix 1.0 发布了。&lt;/p&gt;

&lt;p&gt;Fix 是一个 Ruby 的规范框架。它让你用 &lt;a href="https://www.rfc-editor.org/rfc/rfc2119" rel="nofollow" target="_blank" title=""&gt;RFC 2119&lt;/a&gt; 的词汇——MUST、SHOULD、MAY——写下一个对象应该做什么，而不涉及它是怎么实现的。规范写好之后，拿任何一个对象去测试它，每条期望得到一个结果。&lt;/p&gt;
&lt;h2 id="一个例子"&gt;一个例子&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"fix"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;walk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Klop klop!"&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;swim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Swoosh..."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Fix&lt;/span&gt; &lt;span class="ss"&gt;:Duck&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:walk&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="no"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Klop klop!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:swim&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="no"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Swoosh..."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:speak&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="no"&gt;MAY&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="s2"&gt;"Quaaaaaack!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Fix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Duck&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Duck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ruby duck_fix.rb
&lt;span class="go"&gt;duck_fix.rb:10: Success: expected "Klop klop!" to eq "Klop klop!"
duck_fix.rb:14: Success: expected "Swoosh..." to eq "Swoosh..."
duck_fix.rb:18: Info: NoMethodError: undefined method 'speak' for an instance of Duck
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Fix :Duck do … end&lt;/code&gt; 构建一份规范，并以这个名字注册；&lt;code&gt;Fix[:Duck]&lt;/code&gt; 把它找回来。&lt;code&gt;on :walk&lt;/code&gt; 向主体发送 &lt;code&gt;walk&lt;/code&gt;，里面的 &lt;code&gt;it&lt;/code&gt; 判断返回值。&lt;code&gt;it&lt;/code&gt; 后面不是块，而是一个要求：一个级别，然后一个匹配器。&lt;/p&gt;

&lt;p&gt;鸭子不会说话，但规范只说它 MAY 说话：方法不存在，结果是一条 Info，规范依然满足，进程退出码为 0。换成 MUST，就是一条 Error，退出码为 1。&lt;/p&gt;
&lt;h2 id="五种结果"&gt;五种结果&lt;/h2&gt;
&lt;p&gt;两个问题把级别区分开来：方法存在吗？MUST 和 SHOULD 要求它存在，MAY 不要求。它做了规范说的事吗？MUST 和 MAY 要求做到，SHOULD 只是建议。于是有五种结果：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;级别&lt;/th&gt;
&lt;th&gt;匹配&lt;/th&gt;
&lt;th&gt;不匹配&lt;/th&gt;
&lt;th&gt;方法不存在&lt;/th&gt;
&lt;th&gt;其他异常&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MUST&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Failure&lt;/td&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHOULD&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAY&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Failure&lt;/td&gt;
&lt;td&gt;Info&lt;/td&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Success、Warning、Info 通过；Failure、Error 不通过。&lt;/p&gt;
&lt;h2 id="设计上的几个选择"&gt;设计上的几个选择&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;规范与主体分开。&lt;/strong&gt; 规范是一份文档，加载时读一遍；主体由 &lt;code&gt;test&lt;/code&gt; 的块构建。同一份规范可以测试 &lt;code&gt;Duck.new&lt;/code&gt;，也可以测试任何别的对象。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;每个上下文一个子进程。&lt;/strong&gt; 每个有期望的上下文都在 fork 出来的子进程里运行：主体留下的任何状态都碰不到下一个上下文，主体抛出的异常（包括 &lt;code&gt;exit&lt;/code&gt;）只是它那一条期望的错误。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;输出只有结果。&lt;/strong&gt; 每条期望一行：&lt;code&gt;位置: 结果: 摘要&lt;/code&gt;，错误的 backtrace 缩进在下面。没有颜色，没有点，没有计数。&lt;code&gt;test&lt;/code&gt; 返回是否全部通过，&lt;code&gt;exit&lt;/code&gt; 把它变成进程的退出码。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;很小。&lt;/strong&gt; &lt;code&gt;lib/&lt;/code&gt; 下 9 个文件，约 300 行代码。Fix 站在四个各自独立的 gem 上：&lt;a href="https://github.com/fixrb/aw" rel="nofollow" target="_blank" title=""&gt;Aw&lt;/a&gt;（在子进程里运行一个块，带回结果）、&lt;a href="https://github.com/fixrb/defi" rel="nofollow" target="_blank" title=""&gt;Defi&lt;/a&gt;（把一次方法调用当作值）、&lt;a href="https://github.com/fixrb/matchi" rel="nofollow" target="_blank" title=""&gt;Matchi&lt;/a&gt;（匹配器）、&lt;a href="https://github.com/fixrb/spectus" rel="nofollow" target="_blank" title=""&gt;Spectus&lt;/a&gt;（RFC 2119 的级别与判定）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;规范还可以用 &lt;code&gt;let&lt;/code&gt; 和 &lt;code&gt;with&lt;/code&gt; 给主体传参数，可以用 &lt;code&gt;fix(:Name)&lt;/code&gt; 要求另一份规范。&lt;a href="https://github.com/fixrb/fix-command" rel="nofollow" target="_blank" title=""&gt;fix-command&lt;/a&gt; 提供 &lt;code&gt;fix&lt;/code&gt; 命令，把 &lt;code&gt;fix/&lt;/code&gt; 目录下的每个 &lt;code&gt;*_fix.rb&lt;/code&gt; 各自放在一个进程里运行。这些都在 README 里。&lt;/p&gt;
&lt;h2 id="开始"&gt;开始&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"fix"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要 Ruby 3.3 或更高版本，以及一个支持 &lt;code&gt;fork&lt;/code&gt; 的系统。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;源码：&lt;a href="https://github.com/fixrb/fix" rel="nofollow" target="_blank"&gt;https://github.com/fixrb/fix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;文档：&lt;a href="https://rubydoc.info/gems/fix" rel="nofollow" target="_blank"&gt;https://rubydoc.info/gems/fix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;网站：&lt;a href="https://fixrb.dev/" rel="nofollow" target="_blank"&gt;https://fixrb.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个项目我写了十二年，1.0 是它第一次让我觉得没有什么可以再去掉的。欢迎试用，欢迎反馈。&lt;/p&gt;</description>
      <author>cyri_</author>
      <pubDate>Thu, 05 Nov 2026 23:13:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/44749</link>
      <guid>https://ruby-china.org/topics/44749</guid>
    </item>
    <item>
      <title>从测试框架中期待什么 Ruby 代码？</title>
      <description>&lt;p&gt;你知道当你使用你喜欢的测试框架的&lt;a href="https://zh.wikipedia.org/wiki/%E9%A2%86%E5%9F%9F%E7%89%B9%E5%AE%9A%E8%AF%AD%E8%A8%80" rel="nofollow" target="_blank" title=""&gt;DSL&lt;/a&gt;时，会产生什么样的代码吗？&lt;/p&gt;

&lt;p&gt;作为一个开发者，你可能有兴趣知道。也许直接写出来太吓人了。或者它可能比 minitest 测试集更轻。&lt;/p&gt;

&lt;p&gt;总之，为了尝试回答这个问题，同时避免 RSpec 和 minitest 之间无休止的争论，我将在本文中使用&lt;a href="https://r-spec.dev/" rel="nofollow" target="_blank" title=""&gt;RSpec clone&lt;/a&gt; 项目。&lt;/p&gt;
&lt;h2 id="什么是RSpec clone？"&gt;什么是 RSpec clone？&lt;/h2&gt;
&lt;p&gt;这个项目是对 RSpec 的一个最小化的重新实现，强调正确性、安全性和标准化。&lt;/p&gt;

&lt;p&gt;RSpec clone 试图实现社区&lt;a href="https://rspec.rubystyle.guide/" rel="nofollow" target="_blank" title=""&gt;RSpec 风格指南中列出的准则和最佳实践&lt;/a&gt;。大多数 RSpec 的 DSL 的实现是为了表达所需的结果。没有什么神奇的力量。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/cyri_/a2af913b-4420-4488-8b0b-7312aa560cd7.jpg!large" title="" alt="What did you RSpec?"&gt;&lt;/p&gt;

&lt;p&gt;为了快速了解该框架是如何构建和执行测试的，我将向你展示 DSL 语法和生成的 Ruby 代码之间的对应关系。为了便于阅读，这些对应关系是按方法分组的。&lt;/p&gt;
&lt;h2 id="从DSL到Ruby"&gt;从 DSL 到 Ruby&lt;/h2&gt;&lt;h3 id="describe 该方法"&gt;
&lt;code&gt;describe&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;described_class&lt;/span&gt;
    &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="context 该方法"&gt;
&lt;code&gt;context&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"when in a context"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="subject 该方法"&gt;
&lt;code&gt;subject&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;".subject"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="s2"&gt;"foo"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subject&lt;/span&gt;
    &lt;span class="s2"&gt;"foo"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="嵌入的 describe 该方法"&gt;嵌入的 &lt;code&gt;describe&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;".describe"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"embedded describe"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="let 该方法"&gt;
&lt;code&gt;let&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;".let"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:answer_to_everything&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_to_everything&lt;/span&gt;
    &lt;span class="mi"&gt;41&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;one&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="before 该方法"&gt;
&lt;code&gt;before&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;".before"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="expect 该方法"&gt;
&lt;code&gt;expect&lt;/code&gt; 该方法&lt;/h3&gt;
&lt;p&gt;当我写：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#expect"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望能产生这样的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"matchi/rspec"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"r_spec/clone/expectation_target"&lt;/span&gt;

&lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Matchi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helper&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;ExpectationTarget&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而且我希望看到这种信息：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Success: expected to be 42.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="用Ruby测试是很有趣的!"&gt;用 Ruby 测试是很有趣的！&lt;/h2&gt;
&lt;p&gt;除非&lt;a href="https://github.com/fixrb/brutal" rel="nofollow" target="_blank" title=""&gt;你不写测试&lt;/a&gt;，使用这样一个 DSL 接口的好处是，它限制了在规范中引入可能导致错误的额外逻辑，鼓励使用算法复杂度较低的良好的 Ruby 模式，并使 Ruby 代码更短，更（机器）可读。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://speakerdeck.com/ahawkins/bow-before-minitest" rel="nofollow" target="_blank" title=""&gt;来自 Adam Hawkins&lt;/a&gt;、&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;RSpec 是一种特定领域的测试语言，而 minitest 是 Ruby。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我希望你喜欢&lt;a href="https://r-spec.dev/" rel="nofollow" target="_blank" title=""&gt;RSpec clone&lt;/a&gt;，就像我喜欢这两样。谢谢你。&lt;/p&gt;</description>
      <author>cyri_</author>
      <pubDate>Sat, 03 Jul 2021 22:52:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/41441</link>
      <guid>https://ruby-china.org/topics/41441</guid>
    </item>
    <item>
      <title>用 TestTube 进行 Ruby 代码测试</title>
      <description>&lt;p&gt;我要介绍 &lt;strong&gt;&lt;a href="https://github.com/fixrb/test_tube" rel="nofollow" target="_blank" title=""&gt;TestTube&lt;/a&gt;&lt;/strong&gt; 🧪&lt;/p&gt;

&lt;p&gt;这个简单的测试工具就是 &lt;a href="https://github.com/fixrb/spectus" rel="nofollow" target="_blank" title=""&gt;Spectus&lt;/a&gt; 引擎。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://github.com/fixrb/test_tube/raw/v1.1.0/img/social-media-preview.png" title="" alt="实验代码"&gt;&lt;/p&gt;

&lt;p&gt;要使用 TestTube，需要一个匹配器。
例如，来自 &lt;a href="https://github.com/fixrb/matchi" rel="nofollow" target="_blank" title=""&gt;Matchi&lt;/a&gt;，有一个集合。&lt;/p&gt;

&lt;p&gt;现在，让我们使用这个自定义匹配器：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BeTheAnswer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;match?&lt;/span&gt;
    &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们定义这个块：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;block_of_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"101010"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在让我们看看结果：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_tube"&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TestTube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;matcher: &lt;/span&gt;&lt;span class="no"&gt;BeTheAnswer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;negate: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block_of_code&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actual&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 42&lt;/span&gt;
&lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt; nil&lt;/span&gt;
&lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;got&lt;/span&gt;   &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功！做得好！&lt;/p&gt;

&lt;p&gt;谢谢你的阅读。&lt;/p&gt;</description>
      <author>cyri_</author>
      <pubDate>Mon, 21 Jun 2021 03:44:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/41390</link>
      <guid>https://ruby-china.org/topics/41390</guid>
    </item>
    <item>
      <title>简介 Fix</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;为规范在 Ruby 中的新工具。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fix 的是一个最低限度的测试工具。这是不到 200 行代码。它是基于&lt;a href="https://github.com/fixrb/spectus" rel="nofollow" target="_blank" title=""&gt;Spectus&lt;/a&gt;，&lt;a href="https://github.com/fixrb/matchi" rel="nofollow" target="_blank" title=""&gt;Matchi&lt;/a&gt;等的&lt;a href="https://github.com/fixrb/defi" rel="nofollow" target="_blank" title=""&gt;Defi&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这里是代码的一个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Monster&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;boo: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Boo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;life: &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;mana: &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;hasu: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Hasu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;life: &lt;/span&gt;&lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;mana: &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;随着 Fix 的，我们可以写这个测试：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'monster'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'fix'&lt;/span&gt;

&lt;span class="no"&gt;Fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Monster&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:keys&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;eql&lt;/span&gt; &lt;span class="sx"&gt;%i(boo hasu)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boo&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;eql&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Boo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;life: &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;mana: &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boom&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;raise_exception&lt;/span&gt; &lt;span class="no"&gt;KeyError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面是结果：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby monster_fix_spec.rb
...

Ran 3 tests in 0.000411 seconds
100% compliant - 0 infos, 0 failures, 0 errors
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;欲了解更多详情，您可以在&lt;a href="http://www.rubydoc.info/gems/fix" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;找到的文档。&lt;/p&gt;

&lt;p&gt;快乐编码!!&lt;/p&gt;</description>
      <author>cyri_</author>
      <pubDate>Mon, 05 Oct 2015 22:26:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/27557</link>
      <guid>https://ruby-china.org/topics/27557</guid>
    </item>
  </channel>
</rss>
