<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://fixrb.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://fixrb.dev/" rel="alternate" type="text/html" /><updated>2025-01-03T21:57:04+00:00</updated><id>https://fixrb.dev/feed.xml</id><title type="html">Fix specing framework</title><subtitle>Fix is a modern Ruby testing framework built around a key architectural principle: the complete separation between specifications and tests. It allows you to write pure specification documents that define expected behaviors, and then independently challenge any implementation against these specifications.
</subtitle><author><name>Cyril Kato</name></author><entry><title type="html">The Three Levels of Requirements Inspired by RFC 2119</title><link href="https://fixrb.dev/framework/testing/2024/12/30/the-three-levels-of-requirements-inspired-by-rfc-2119.html" rel="alternate" type="text/html" title="The Three Levels of Requirements Inspired by RFC 2119" /><published>2024-12-30T00:00:00+00:00</published><updated>2024-12-30T00:00:00+00:00</updated><id>https://fixrb.dev/framework/testing/2024/12/30/the-three-levels-of-requirements-inspired-by-rfc-2119</id><content type="html" xml:base="https://fixrb.dev/framework/testing/2024/12/30/the-three-levels-of-requirements-inspired-by-rfc-2119.html"><![CDATA[<p>In software development, precision in language is crucial. This is particularly true when defining test specifications. Fix, through the Spectus library, introduces three levels of requirements directly inspired by RFC 2119, offering rich and nuanced semantics for your specifications.</p>

<h2 id="the-three-requirement-levels">The Three Requirement Levels</h2>

<h3 id="mustmust_not-the-absolute">MUST/MUST_NOT: The Absolute</h3>

<p>The MUST level represents an absolute requirement of the specification. When you use MUST, you indicate that a feature is mandatory and non-negotiable. Formally, for a test using MUST to pass, the expectation must be conclusively met. If the expectation fails, the test fails unconditionally.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Fix</span> <span class="ss">:Password</span> <span class="k">do</span>
  <span class="n">it</span> <span class="no">MUST_NOT</span> <span class="n">be_empty</span>
  <span class="n">it</span> <span class="no">MUST</span> <span class="n">have_length_of</span><span class="p">(</span><span class="mi">8</span><span class="o">..</span><span class="mi">128</span><span class="p">)</span>

  <span class="n">with</span> <span class="ss">content: </span><span class="s2">"password123"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">include_number</span>
    <span class="n">it</span> <span class="no">MUST_NOT</span> <span class="n">be_common_password</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In this example, each test marked with MUST represents a rule that cannot be broken. An empty or too short password is simply not acceptable. The test will fail if any of these conditions are not met, regardless of any other circumstances.</p>

<h3 id="shouldshould_not-the-recommendation">SHOULD/SHOULD_NOT: The Recommendation</h3>

<p>SHOULD indicates a strong recommendation that can be ignored in particular circumstances, but whose implications must be understood and carefully evaluated. From a technical standpoint, if an expectation marked with SHOULD fails, the test can still pass if no exception is raised. This allows for graceful degradation of optional but recommended features.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Fix</span> <span class="ss">:EmailService</span> <span class="k">do</span>
  <span class="n">on</span> <span class="ss">:send</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"user@example.com"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">deliver_email</span>
    <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">be_rate_limited</span>
    <span class="n">it</span> <span class="no">SHOULD_NOT</span> <span class="n">take_longer_than</span><span class="p">(</span><span class="mi">5</span><span class="p">.</span><span class="nf">seconds</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Here, while email delivery is mandatory (MUST), rate limiting is strongly recommended (SHOULD) but could be disabled in specific contexts. If the rate limiting check fails but doesn’t raise an exception, the test will still pass, indicating that while the recommendation wasn’t followed, the system is still functioning as expected.</p>

<h3 id="may-the-optional">MAY: The Optional</h3>

<p>MAY indicates a truly optional feature. In Fix’s implementation, a MAY requirement has a unique behavior: the test will pass either if the expectation is met OR if a NoMethodError is raised. This elegantly handles cases where optional features are not implemented at all. This is particularly valuable for specifying features that might be implemented differently across various contexts or might not be implemented at all.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Fix</span> <span class="ss">:UserProfile</span> <span class="k">do</span>
  <span class="n">on</span> <span class="ss">:avatar</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">be_valid_image</span>
    <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">be_less_than</span><span class="p">(</span><span class="mi">5</span><span class="p">.</span><span class="nf">megabytes</span><span class="p">)</span>
    <span class="n">it</span> <span class="no">MAY</span> <span class="n">be_square</span>
    <span class="n">it</span> <span class="no">MAY</span> <span class="n">support_animation</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In this example:</p>
<ul>
  <li>The avatar must be a valid image (MUST) - this will fail if not met</li>
  <li>It should be lightweight (SHOULD) - this will pass if it fails without exception</li>
  <li>It may be square (MAY) - this will pass if either:
    <ol>
      <li>The expectation is met (the avatar is square)</li>
      <li>The method to check squareness isn’t implemented (raises NoMethodError)</li>
    </ol>
  </li>
  <li>Similarly, animation support is optional and can be entirely unimplemented</li>
</ul>

<p>This three-level system allows for precise specification of requirements while maintaining flexibility in implementation. Here’s a more complex example that demonstrates all three levels working together:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Fix</span> <span class="ss">:Document</span> <span class="k">do</span>
  <span class="c1"># Absolute requirements - must pass their expectations</span>
  <span class="n">it</span> <span class="no">MUST</span> <span class="n">have_content</span>
  <span class="n">it</span> <span class="no">MUST</span> <span class="n">have_created_at</span>

  <span class="c1"># Strong recommendations - can fail without exception</span>
  <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">have_author</span>
  <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">be_versioned</span>

  <span class="c1"># Optional features - can be unimplemented</span>
  <span class="n">it</span> <span class="no">MAY</span> <span class="n">be_encryptable</span>
  <span class="n">it</span> <span class="no">MAY</span> <span class="n">support_collaborative_editing</span>

  <span class="n">on</span> <span class="ss">:publish</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">change</span><span class="p">(</span><span class="n">document</span><span class="p">,</span> <span class="ss">:status</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="ss">:published</span><span class="p">)</span>  <span class="c1"># Must succeed</span>
    <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">notify_subscribers</span>             <span class="c1"># Can fail gracefully</span>
    <span class="n">it</span> <span class="no">MAY</span> <span class="n">trigger_indexing</span>                  <span class="c1"># Can be unimplemented</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="historical-evolution-from-rspec-to-fix">Historical Evolution: From RSpec to Fix</h2>

<p>This semantic approach contrasts with RSpec’s history. Originally, <a href="https://github.com/rspec/rspec-expectations/blob/ba31727e856de42abb5a2e6566855f0831e1a619/Should.md">RSpec used the <code class="language-plaintext highlighter-rouge">should</code> keyword</a> as its main interface:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Old RSpec style</span>
<span class="n">describe</span> <span class="no">User</span> <span class="k">do</span>
  <span class="n">it</span> <span class="s2">"should validate email"</span> <span class="k">do</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">email</span> <span class="o">=</span> <span class="s2">"invalid"</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">should_not</span> <span class="n">be_valid</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>However, this approach had several issues:</p>
<ul>
  <li>Monkey-patching <code class="language-plaintext highlighter-rouge">Object</code> to add <code class="language-plaintext highlighter-rouge">should</code> could cause conflicts</li>
  <li>Using <code class="language-plaintext highlighter-rouge">should</code> for absolute requirements was semantically incorrect</li>
  <li>Code became harder to maintain due to global namespace pollution</li>
</ul>

<p>RSpec eventually migrated to the <code class="language-plaintext highlighter-rouge">expect</code> syntax:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Modern RSpec</span>
<span class="n">describe</span> <span class="no">User</span> <span class="k">do</span>
  <span class="n">it</span> <span class="s2">"validates email"</span> <span class="k">do</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">email</span> <span class="o">=</span> <span class="s2">"invalid"</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">user</span><span class="p">).</span><span class="nf">not_to</span> <span class="n">be_valid</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="the-fix-approach-clarity-and-precision">The Fix Approach: Clarity and Precision</h2>

<p>Fix takes a different path by fully embracing RFC 2119 semantics. Here’s a complete example illustrating all three levels:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Fix</span> <span class="ss">:Article</span> <span class="k">do</span>
  <span class="c1"># Absolute requirements</span>
  <span class="n">it</span> <span class="no">MUST</span> <span class="n">have_title</span>
  <span class="n">it</span> <span class="no">MUST</span> <span class="n">have_content</span>

  <span class="c1"># Strong recommendations</span>
  <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">have_meta_description</span>
  <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">be_properly_formatted</span>

  <span class="c1"># Optional features</span>
  <span class="n">it</span> <span class="no">MAY</span> <span class="n">have_cover_image</span>
  <span class="n">it</span> <span class="no">MAY</span> <span class="n">have_comments_enabled</span>

  <span class="n">on</span> <span class="ss">:publish</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">change</span><span class="p">(</span><span class="n">article</span><span class="p">,</span> <span class="ss">:status</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="ss">:published</span><span class="p">)</span>
    <span class="n">it</span> <span class="no">SHOULD</span> <span class="n">trigger_notification</span>
    <span class="n">it</span> <span class="no">MAY</span> <span class="n">be_featured</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># Test against a specific implementation</span>
<span class="no">Fix</span><span class="p">[</span><span class="ss">:Article</span><span class="p">].</span><span class="nf">test</span> <span class="p">{</span> <span class="no">Article</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">title: </span><span class="s2">"Test"</span><span class="p">,</span> <span class="ss">content: </span><span class="s2">"Content"</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>

<p>This approach offers several advantages:</p>
<ul>
  <li>Clear and precise semantics for each requirement level</li>
  <li>No global monkey-patching</li>
  <li>Living documentation that exactly reflects developer intentions</li>
  <li>Better team communication through standardized vocabulary</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Fix’s three requirement levels, inherited from Spectus, offer a powerful and nuanced way to express your testing expectations. This approach, combined with the clear separation between specifications and implementations, makes Fix particularly well-suited for writing maintainable and communicative tests.</p>

<p>RSpec’s evolution shows us the importance of precise semantics and clean architecture. Fix capitalizes on these lessons while offering a modern and elegant approach to Ruby testing.</p>]]></content><author><name>Cyril Kato</name></author><category term="framework" /><category term="testing" /><category term="fix" /><category term="rspec" /><category term="spectus" /><category term="testing" /><category term="must" /><category term="should" /><category term="may" /><summary type="html"><![CDATA[Discover how Fix implements MUST, SHOULD, and MAY requirement levels for more precise specifications, and how this approach compares to RSpec's historical evolution.]]></summary></entry><entry><title type="html">Rethinking Test Architecture with Clear Specification Separation</title><link href="https://fixrb.dev/framework/testing/2024/12/29/rethinking-test-architecture-with-fix.html" rel="alternate" type="text/html" title="Rethinking Test Architecture with Clear Specification Separation" /><published>2024-12-29T00:00:00+00:00</published><updated>2024-12-29T00:00:00+00:00</updated><id>https://fixrb.dev/framework/testing/2024/12/29/rethinking-test-architecture-with-fix</id><content type="html" xml:base="https://fixrb.dev/framework/testing/2024/12/29/rethinking-test-architecture-with-fix.html"><![CDATA[<p>In the Ruby development world, testing frameworks like RSpec or Minitest have become industry standards. However, Fix proposes a radically different approach that deserves our attention: a clean separation between specifications and their implementation.</p>

<h2 id="traditional-architecture-a-mix-of-concerns">Traditional Architecture: A Mix of Concerns</h2>

<p>Traditionally, Ruby testing frameworks mix the definition of expected behaviors and the concrete examples that verify them in the same file. Let’s look at a classic example with RSpec:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">Calculator</span> <span class="k">do</span>
  <span class="n">subject</span><span class="p">(</span><span class="ss">:calculator</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">described_class</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">first_number</span><span class="p">)</span>
  <span class="k">end</span>
  <span class="n">describe</span> <span class="s2">"when first number is 2"</span> <span class="k">do</span>
    <span class="n">let</span><span class="p">(</span><span class="ss">:first_number</span><span class="p">)</span> <span class="k">do</span>
      <span class="mi">2</span>
    <span class="k">end</span>
    <span class="n">context</span> <span class="s2">"#add"</span> <span class="k">do</span>
      <span class="n">let</span><span class="p">(</span><span class="ss">:second_number</span><span class="p">)</span> <span class="k">do</span>
        <span class="mi">3</span>
      <span class="k">end</span>
      <span class="n">it</span> <span class="s2">"adds two numbers"</span> <span class="k">do</span>
        <span class="n">expect</span><span class="p">(</span><span class="n">calculator</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">second_number</span><span class="p">)).</span><span class="nf">to</span> <span class="n">be</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In this example, the specification (adding two numbers) is wrapped in several layers of setup and context, mixing the what (adding numbers should equal 5) with the how (creating instances, setting up variables).</p>

<h2 id="the-fix-approach-a-clear-separation-of-responsibilities">The Fix Approach: A Clear Separation of Responsibilities</h2>

<p>Fix adopts a radically different philosophy by clearly dividing two aspects:</p>

<ol>
  <li>Specifications: pure documents that define expected behaviors</li>
  <li>Tests: concrete implementations that challenge these specifications</li>
</ol>

<p>Here’s how the same calculator test looks with Fix:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 1. The specification - a pure and reusable document</span>
<span class="no">Fix</span> <span class="ss">:Calculator</span> <span class="k">do</span>
  <span class="n">on</span><span class="p">(</span><span class="ss">:new</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="k">do</span>
    <span class="n">on</span><span class="p">(</span><span class="ss">:add</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="k">do</span>
      <span class="n">it</span> <span class="no">MUST</span> <span class="n">be</span> <span class="mi">5</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># 2. The test - a specific implementation</span>
<span class="no">Fix</span><span class="p">[</span><span class="ss">:Calculator</span><span class="p">].</span><span class="nf">test</span> <span class="p">{</span> <span class="no">Calculator</span> <span class="p">}</span>
</code></pre></div></div>

<p>The contrast is striking. Fix’s specification is:</p>
<ul>
  <li>Concise: the expected behavior is expressed in just a few lines</li>
  <li>Clear: the flow from constructor to method call is immediately apparent</li>
  <li>Pure: it describes only what should happen, not how to set it up</li>
</ul>

<p>This separation brings several major advantages:</p>

<h3 id="1-specification-reusability">1. Specification Reusability</h3>

<p>Specifications become autonomous documents that can be reused across different implementations. The same calculator specification could be used to test different calculator classes as long as they follow the same interface.</p>

<h3 id="2-isolated-testing">2. Isolated Testing</h3>

<p>Each test can be executed in complete isolation, which makes debugging easier and improves test code maintainability. The separation between specification and implementation means you can change how you test without changing what you’re testing.</p>

<h2 id="a-more-complex-example">A More Complex Example</h2>

<p>Let’s see how this separation applies in a more elaborate case:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># A generic specification for a payment system</span>
<span class="no">Fix</span> <span class="ss">:PaymentSystem</span> <span class="k">do</span>
  <span class="n">with</span> <span class="ss">amount: </span><span class="mi">100</span> <span class="k">do</span>
    <span class="n">it</span> <span class="no">MUST</span> <span class="n">be_positive</span>

    <span class="n">on</span> <span class="ss">:process</span> <span class="k">do</span>
      <span class="n">it</span> <span class="no">MUST</span> <span class="n">be_successful</span>
      <span class="n">it</span> <span class="no">MUST</span> <span class="n">change</span><span class="p">(</span><span class="n">account</span><span class="p">,</span> <span class="ss">:balance</span><span class="p">).</span><span class="nf">by</span><span class="p">(</span><span class="o">-</span><span class="mi">100</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="n">with</span> <span class="ss">amount: </span><span class="o">-</span><span class="mi">50</span> <span class="k">do</span>
    <span class="n">on</span> <span class="ss">:process</span> <span class="k">do</span>
      <span class="n">it</span> <span class="no">MUST</span> <span class="n">raise_exception</span><span class="p">(</span><span class="no">InvalidAmountError</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># Specific tests for different implementations</span>
<span class="no">Fix</span><span class="p">[</span><span class="ss">:PaymentSystem</span><span class="p">].</span><span class="nf">test</span> <span class="p">{</span> <span class="no">StripePayment</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">amount: </span><span class="mi">100</span><span class="p">)</span> <span class="p">}</span>
<span class="no">Fix</span><span class="p">[</span><span class="ss">:PaymentSystem</span><span class="p">].</span><span class="nf">test</span> <span class="p">{</span> <span class="no">PaypalPayment</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">amount: </span><span class="mi">100</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>

<p>In this example, the payment system specification is generic and can be applied to different payment implementations. By focusing on the interface rather than the implementation details, we create a reusable contract that any payment processor can fulfill.</p>

<h2 id="benefits-in-practice">Benefits in Practice</h2>

<p>This architectural separation provides several practical benefits:</p>

<ol>
  <li>
    <p><strong>Documentation</strong>: Specifications serve as clear, living documentation of your system’s expected behavior</p>
  </li>
  <li>
    <p><strong>Maintainability</strong>: Changes to test implementation don’t require changes to specifications</p>
  </li>
  <li><strong>Flexibility</strong>: The same specifications can verify multiple implementations, making it ideal for:
    <ul>
      <li>Testing different versions of a class</li>
      <li>Verifying third-party integrations</li>
      <li>Ensuring consistency across microservices</li>
    </ul>
  </li>
  <li><strong>Clarity</strong>: By separating what from how, both specifications and tests become more focused and easier to understand</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>Fix invites us to rethink how we write our tests in Ruby. By clearly separating specifications from concrete tests, it allows us to:</p>

<ul>
  <li>Create clearer and reusable specifications</li>
  <li>Maintain living documentation of our expectations</li>
  <li>Test different implementations against the same specifications</li>
  <li>Isolate problems more easily</li>
</ul>

<p>This unique architectural approach makes Fix a particularly interesting tool for projects that require precise and reusable specifications while maintaining great flexibility in their implementation.</p>]]></content><author><name>Cyril Kato</name></author><category term="framework" /><category term="testing" /><category term="fix" /><category term="ruby" /><category term="specifications" /><category term="architecture" /><category term="testing framework" /><summary type="html"><![CDATA[Discover how Fix's unique approach to testing in Ruby promotes clear separation between specifications and implementation, making your test suite more maintainable and reusable.]]></summary></entry><entry><title type="html">The Fundamental Distinction Between Expected and Actual Values in Testing</title><link href="https://fixrb.dev/design/testing/2024/11/04/fundamental-distinction-between-expected-and-actual-values-in-testing.html" rel="alternate" type="text/html" title="The Fundamental Distinction Between Expected and Actual Values in Testing" /><published>2024-11-04T11:00:00+00:00</published><updated>2024-11-04T11:00:00+00:00</updated><id>https://fixrb.dev/design/testing/2024/11/04/fundamental-distinction-between-expected-and-actual-values-in-testing</id><content type="html" xml:base="https://fixrb.dev/design/testing/2024/11/04/fundamental-distinction-between-expected-and-actual-values-in-testing.html"><![CDATA[<p>The distinction between expected and actual values in automated testing is more than just a convention - it’s a fundamental architectural principle that deserves our attention.</p>

<p>To emphasize the semantic difference between these two values, we must understand that the expected value is a known constant, an integral part of the specification. In contrast, the actual value, obtained by challenging a foreign object, must be evaluated against the expected value by the predicate. This is precisely the role of the latter: to ensure that the obtained value satisfies the criteria defined by the expected value.</p>

<p>This distinction is so fundamental that it must be honored in the very design of the predicate: it receives the expected value as a reference during initialization, then evaluates the actual value against this reference. This asymmetry in value handling reflects their profoundly different nature, a principle that modern testing libraries like <a href="https://github.com/fixrb/matchi">Matchi</a> have wisely chosen to implement.</p>

<p>Let’s illustrate this principle with a concrete example. Consider a simple equivalence test between two strings:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">EXPECTED_VALUE</span> <span class="o">=</span> <span class="s2">"foo"</span>
<span class="n">actual_value</span> <span class="o">=</span> <span class="s2">"bar"</span>
</code></pre></div></div>

<p>At first glance, these two approaches seem equivalent:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">EXPECTED_VALUE</span><span class="p">.</span><span class="nf">eql?</span><span class="p">(</span><span class="n">actual_value</span><span class="p">)</span> <span class="c1"># =&gt; false</span>
<span class="n">actual_value</span><span class="p">.</span><span class="nf">eql?</span><span class="p">(</span><span class="no">EXPECTED_VALUE</span><span class="p">)</span> <span class="c1"># =&gt; false</span>
</code></pre></div></div>

<p>However, this apparent symmetry can be deceptive. Consider a scenario where the actual value has been compromised:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nc">actual_value</span><span class="o">.</span><span class="nf">eql?</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
  <span class="kp">true</span>
<span class="k">end</span>

<span class="n">actual_value</span><span class="p">.</span><span class="nf">eql?</span><span class="p">(</span><span class="no">EXPECTED_VALUE</span><span class="p">)</span> <span class="c1"># =&gt; true</span>
</code></pre></div></div>

<p>This example, though simplified, highlights a fundamental principle: the predicate must never trust the actual value. Delegating the responsibility of comparison to the latter would mean trusting it for its own evaluation. This is why the predicate must always maintain control of the comparison, using the expected value as a reference to evaluate the actual value, and not the reverse.</p>

<p>This rigorous approach to predicate design contributes not only to the robustness of tests but also to their clarity and long-term maintainability.</p>]]></content><author><name>Cyril Kato</name></author><category term="design" /><category term="testing" /><category term="ruby" /><category term="testing" /><category term="matchi" /><category term="design principles" /><category term="best practices" /><summary type="html"><![CDATA[Explore why the distinction between expected and actual values is crucial in test design, and how it impacts the robustness and reliability of your test suite.]]></summary></entry><entry><title type="html">From RSpec to Fix: A Journey Towards Simpler Testing</title><link href="https://fixrb.dev/framework/comparison/2015/09/06/from-rspec-to-fix.html" rel="alternate" type="text/html" title="From RSpec to Fix: A Journey Towards Simpler Testing" /><published>2015-09-06T11:00:00+00:00</published><updated>2015-09-06T11:00:00+00:00</updated><id>https://fixrb.dev/framework/comparison/2015/09/06/from-rspec-to-fix</id><content type="html" xml:base="https://fixrb.dev/framework/comparison/2015/09/06/from-rspec-to-fix.html"><![CDATA[<h2 id="the-quest-for-simplicity">The Quest for Simplicity</h2>

<p>One of the most striking differences between Fix and traditional testing frameworks like RSpec lies in their complexity. Let’s look at some numbers that tell an interesting story:</p>

<h3 id="rspecs-components-version-33">RSpec’s Components (version 3.3)</h3>

<ul>
  <li><a href="https://github.com/rspec/rspec/releases/tag/v3.3.0">rspec</a> gem (7 LOC) with runtime dependencies:
    <ul>
      <li><a href="https://github.com/rspec/rspec-core/releases/tag/v3.3.2">rspec-core</a>: 6,689 LOC</li>
      <li><a href="https://github.com/rspec/rspec-expectations/releases/tag/v3.3.1">rspec-expectations</a>: 3,697 LOC</li>
      <li><a href="https://github.com/rspec/rspec-mocks/releases/tag/v3.3.2">rspec-mocks</a>: 3,963 LOC</li>
      <li><a href="https://github.com/rspec/rspec-support/releases/tag/v3.3.0">rspec-support</a>: 1,509 LOC</li>
      <li><a href="https://github.com/halostatue/diff-lcs/releases/tag/v1.2.5">diff-lcs</a>: 1,264 LOC</li>
    </ul>
  </li>
</ul>

<p><strong>Total: 17,129 lines of code</strong></p>

<h3 id="fixs-components-version-07">Fix’s Components (version 0.7)</h3>

<ul>
  <li><a href="https://github.com/fixrb/fix/releases/tag/v0.7.0">fix</a> gem (148 LOC) with runtime dependencies:
    <ul>
      <li><a href="https://github.com/fixrb/spectus/releases/tag/v2.3.1">spectus</a>: 289 LOC</li>
      <li><a href="https://github.com/fixrb/matchi/releases/tag/v0.0.9">matchi</a>: 73 LOC</li>
    </ul>
  </li>
</ul>

<p><strong>Total: 510 lines of code</strong></p>

<p>The core philosophy is simple: a testing framework shouldn’t be more complex than the code it tests. This dramatic difference in code size (16,619 lines of code) reflects Fix’s commitment to minimalism and clarity.</p>

<h2 id="a-real-world-comparison">A Real-World Comparison</h2>

<p>Let’s look at a real-world example that demonstrates the key differences in approach. Consider this Monster class:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Monster</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get</span>
    <span class="p">{</span>
      <span class="ss">boo: </span><span class="p">{</span>
        <span class="ss">name: </span><span class="s2">"Boo"</span><span class="p">,</span>
        <span class="ss">life: </span><span class="mi">123</span><span class="p">,</span>
        <span class="ss">mana: </span><span class="mi">42</span>
      <span class="p">},</span>
      <span class="ss">hasu: </span><span class="p">{</span>
        <span class="ss">name: </span><span class="s2">"Hasu"</span><span class="p">,</span>
        <span class="ss">life: </span><span class="mi">88</span><span class="p">,</span>
        <span class="ss">mana: </span><span class="mi">40</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
    <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">get</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="rspecs-layered-approach">RSpec’s Layered Approach</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require_relative</span> <span class="s2">"monster"</span>
<span class="nb">require</span> <span class="s2">"rspec/autorun"</span>

<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">Monster</span> <span class="k">do</span>
  <span class="n">describe</span> <span class="s2">".get"</span> <span class="k">do</span>
    <span class="n">subject</span><span class="p">(</span><span class="ss">:monsters</span><span class="p">)</span> <span class="p">{</span> <span class="n">described_class</span><span class="p">.</span><span class="nf">get</span> <span class="p">}</span>

    <span class="n">describe</span> <span class="s2">"#keys"</span> <span class="k">do</span>
      <span class="n">it</span> <span class="p">{</span> <span class="n">expect</span><span class="p">(</span><span class="n">monsters</span><span class="p">.</span><span class="nf">keys</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span> <span class="sx">%i(boo hasu)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="n">describe</span> <span class="s2">".new"</span> <span class="k">do</span>
    <span class="n">subject</span><span class="p">(</span><span class="ss">:described_instance</span><span class="p">)</span> <span class="p">{</span> <span class="n">described_class</span><span class="p">.</span><span class="nf">new</span> <span class="p">}</span>

    <span class="n">describe</span> <span class="s2">"#get"</span> <span class="k">do</span>
      <span class="n">subject</span><span class="p">(</span><span class="ss">:monster</span><span class="p">)</span> <span class="p">{</span> <span class="n">described_instance</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="p">}</span>

      <span class="n">context</span> <span class="s2">"with Boo monster"</span> <span class="k">do</span>
        <span class="n">let</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span> <span class="p">{</span> <span class="ss">:boo</span> <span class="p">}</span>
        <span class="n">it</span> <span class="p">{</span> <span class="n">expect</span><span class="p">(</span><span class="n">monster</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span><span class="p">({</span> <span class="ss">name: </span><span class="s2">"Boo"</span><span class="p">,</span> <span class="ss">life: </span><span class="mi">123</span><span class="p">,</span> <span class="ss">mana: </span><span class="mi">42</span> <span class="p">})</span> <span class="p">}</span>
      <span class="k">end</span>

      <span class="n">context</span> <span class="s2">"with Boom monster"</span> <span class="k">do</span>
        <span class="n">let</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span> <span class="p">{</span> <span class="ss">:boom</span> <span class="p">}</span>
        <span class="n">it</span> <span class="p">{</span> <span class="n">expect</span> <span class="p">{</span> <span class="n">monster</span> <span class="p">}.</span><span class="nf">to</span> <span class="n">raise_exception</span> <span class="no">KeyError</span> <span class="p">}</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="fixs-direct-style">Fix’s Direct Style</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require_relative</span> <span class="s2">"monster"</span>
<span class="nb">require</span> <span class="s2">"fix"</span>

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

  <span class="n">on</span> <span class="ss">:new</span> <span class="k">do</span>
    <span class="n">on</span> <span class="ss">:get</span><span class="p">,</span> <span class="ss">:boo</span> <span class="k">do</span>
      <span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">eql</span><span class="p">({</span> <span class="ss">name: </span><span class="s2">"Boo"</span><span class="p">,</span> <span class="ss">life: </span><span class="mi">123</span><span class="p">,</span> <span class="ss">mana: </span><span class="mi">42</span> <span class="p">})</span> <span class="p">}</span>
    <span class="k">end</span>

    <span class="n">on</span> <span class="ss">:get</span><span class="p">,</span> <span class="ss">:boom</span> <span class="k">do</span>
      <span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">raise_exception</span> <span class="no">KeyError</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="key-differentiators">Key Differentiators</h2>

<ol>
  <li>
    <p><strong>Method Chaining</strong>: Fix allows describing methods with one expression, whether for class or instance methods. This leads to more concise and readable code.</p>
  </li>
  <li>
    <p><strong>Single Source of Truth</strong>: All specifications are derived from the described front object populated at the root. There’s no need for explicit or implicit subjects - there’s just one read-only dynamic subject deduced from the front object and described methods.</p>
  </li>
  <li>
    <p><strong>Consistent Syntax</strong>: Fix maintains the same syntax regardless of what’s being tested. Whether you’re checking a value, expecting an error, or verifying a state change, the syntax remains uniform and predictable.</p>
  </li>
</ol>

<h2 id="clarity-in-practice">Clarity in Practice</h2>

<p>Fix encourages a more direct and less ceremonial approach to testing. Compare how both frameworks handle error checking:</p>

<p>RSpec:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">expect</span> <span class="p">{</span> <span class="n">problematic_call</span> <span class="p">}.</span><span class="nf">to</span> <span class="n">raise_exception</span><span class="p">(</span><span class="no">ErrorType</span><span class="p">)</span>
</code></pre></div></div>

<p>Fix:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">raise_exception</span> <span class="no">ErrorType</span> <span class="p">}</span>
</code></pre></div></div>

<p>Or value comparison:</p>

<p>RSpec:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">expect</span><span class="p">(</span><span class="n">value</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span><span class="p">(</span><span class="n">expected</span><span class="p">)</span>
</code></pre></div></div>

<p>Fix:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">eql</span> <span class="n">expected</span> <span class="p">}</span>
</code></pre></div></div>

<p>This consistency helps reduce cognitive load and makes tests easier to write and understand.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Fix represents a fresh approach to Ruby testing that prioritizes simplicity and clarity. By reducing complexity and maintaining a consistent syntax, it helps developers focus on what matters: writing clear, maintainable tests that effectively verify their code’s behavior.</p>

<p>Want to try Fix for yourself? Get started with:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>fix
</code></pre></div></div>

<p>Visit our <a href="https://rubydoc.info/gems/fix">documentation</a> to learn more about how Fix can improve your testing workflow.</p>]]></content><author><name>Cyril Kato</name></author><category term="framework" /><category term="comparison" /><category term="fix" /><category term="ruby" /><category term="rspec" /><category term="testing" /><category term="simplicity" /><summary type="html"><![CDATA[Discover how Fix brings simplicity and clarity to Ruby testing with its minimalist approach and intuitive syntax, comparing it with traditional frameworks like RSpec.]]></summary></entry><entry><title type="html">A Fresh Take on Ruby Testing</title><link href="https://fixrb.dev/framework/release/2015/09/03/a-fresh-take-on-ruby-testing.html" rel="alternate" type="text/html" title="A Fresh Take on Ruby Testing" /><published>2015-09-03T11:00:00+00:00</published><updated>2015-09-03T11:00:00+00:00</updated><id>https://fixrb.dev/framework/release/2015/09/03/a-fresh-take-on-ruby-testing</id><content type="html" xml:base="https://fixrb.dev/framework/release/2015/09/03/a-fresh-take-on-ruby-testing.html"><![CDATA[<p>Today marks an exciting milestone in our journey as we announce the release of Fix 0.7! After months of careful development and <a href="https://speakerdeck.com/cyril/konbanha-tiao-jian-yabiheibiatesuto">multiple iterations</a>, we’re proud to present a testing framework that brings simplicity and clarity back to Ruby testing.</p>

<h2 id="the-philosophy-behind-fix">The Philosophy Behind Fix</h2>

<p>Fix emerged from a simple observation: testing frameworks shouldn’t be more complex than the code they’re testing. Built on just 148 lines of code and powered by the <a href="https://github.com/fixrb/spectus">Spectus expectation library</a>, Fix takes a deliberately minimalist approach. We’ve intentionally omitted features like benchmarking and mocking to focus on what matters most: writing clear, maintainable specifications.</p>

<h3 id="key-features">Key Features</h3>

<ul>
  <li><strong>Pure Specification Documents</strong>: Fix treats specs as living documents that remain logic-free and crystal clear</li>
  <li><strong>Version-Resistant</strong>: Specifications remain stable across Fix versions, protecting against software erosion</li>
  <li><strong>No Magic</strong>: We avoid monkey-patching and other Ruby “<a href="https://blog.arkency.com/2013/06/are-we-abusing-at-exit/">magic tricks</a>” that can obscure behavior</li>
  <li><strong>Authentic Ruby Objects</strong>: Work with pure, unmuted Ruby objects for unambiguous and structured specs</li>
</ul>

<h2 id="why-another-testing-framework">Why Another Testing Framework?</h2>

<p>After a decade of Ruby development, I found myself struggling to understand RSpec’s source code. This raised a concerning question: if a testing framework is more complex than the code it tests, how confident can we be in our tests?</p>

<p>Let’s look at a revealing example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">App</span>
  <span class="k">def</span> <span class="nf">equal?</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
    <span class="kp">true</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="nb">require</span> <span class="s2">"rspec"</span>

<span class="no">RSpec</span><span class="p">.</span><span class="nf">describe</span> <span class="no">App</span> <span class="k">do</span>
  <span class="n">it</span> <span class="s2">"is the answer to life, the universe and everything"</span> <span class="k">do</span>
    <span class="n">expect</span><span class="p">(</span><span class="n">described_class</span><span class="p">.</span><span class="nf">new</span><span class="p">).</span><span class="nf">to</span> <span class="n">equal</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Running this with RSpec:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rspec wat_spec.rb
<span class="nb">.</span>

Finished <span class="k">in </span>0.00146 seconds <span class="o">(</span>files took 0.17203 seconds to load<span class="o">)</span>
1 example, 0 failures
</code></pre></div></div>

<p>Surprisingly, RSpec tells us that <code class="language-plaintext highlighter-rouge">App.new</code> equals 42! While this specific issue could be resolved by <a href="https://github.com/rspec/rspec-expectations/blob/995d1acd5161d94d28f6af9835b79c9d9e586307/lib/rspec/matchers/built_in/equal.rb#L40">reversing actual and expected values</a>, it highlights potential risks in complex testing frameworks.</p>

<h2 id="fix-in-action">Fix in Action</h2>

<p>Let’s see how Fix handles a real-world test case:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># car_spec.rb</span>
<span class="nb">require</span> <span class="s2">"fix"</span>

<span class="no">Fix</span> <span class="ss">:Car</span> <span class="k">do</span>
  <span class="n">on</span> <span class="ss">:new</span><span class="p">,</span> <span class="ss">color: </span><span class="s2">"red"</span> <span class="k">do</span>
    <span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">be_an_instance_of</span> <span class="no">Car</span> <span class="p">}</span>

    <span class="n">on</span> <span class="ss">:color</span> <span class="k">do</span>
      <span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">eql</span> <span class="s2">"red"</span> <span class="p">}</span>
    <span class="k">end</span>

    <span class="n">on</span> <span class="ss">:start</span> <span class="k">do</span>
      <span class="n">it</span> <span class="p">{</span> <span class="no">MUST</span> <span class="n">change</span><span class="p">(</span><span class="n">car</span><span class="p">,</span> <span class="ss">:running?</span><span class="p">).</span><span class="nf">from</span><span class="p">(</span><span class="kp">false</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># Running the specification</span>
<span class="no">Fix</span><span class="p">[</span><span class="ss">:Car</span><span class="p">].</span><span class="nf">test</span> <span class="p">{</span> <span class="no">Car</span> <span class="p">}</span>
</code></pre></div></div>

<p>Notice how Fix:</p>
<ul>
  <li>Keeps specifications clear and concise</li>
  <li>Uses method chaining for natural readability</li>
  <li>Focuses on behavior rather than implementation details</li>
  <li>Maintains consistent syntax across different types of tests</li>
</ul>

<h2 id="looking-forward">Looking Forward</h2>

<p>As we approach version 1.0.0, our focus remains on stability and refinement rather than adding new features. Fix is ready for production use, and we’re excited to see how the Ruby community puts it to work.</p>

<h3 id="key-areas-of-focus">Key Areas of Focus</h3>

<ol>
  <li><strong>Documentation Enhancement</strong>: Making it easier for newcomers to get started</li>
  <li><strong>Performance Optimization</strong>: Ensuring Fix remains lightweight and fast</li>
  <li><strong>Community Feedback</strong>: Incorporating real-world usage patterns</li>
  <li><strong>Ecosystem Growth</strong>: Building tools and extensions around the core framework</li>
</ol>

<h2 id="get-involved">Get Involved</h2>

<ul>
  <li>Try Fix in your projects: <code class="language-plaintext highlighter-rouge">gem install fix</code></li>
  <li><a href="https://github.com/fixrb/fix">Visit our GitHub repository</a></li>
  <li>Share your feedback - we value all perspectives!</li>
</ul>

<p>Whether you love it or see room for improvement, we want to hear from you. Your feedback will help shape the future of Fix.</p>

<p>Happy testing!</p>]]></content><author><name>Cyril Kato</name></author><category term="framework" /><category term="release" /><category term="fix" /><category term="ruby" /><category term="testing" /><category term="rspec" /><category term="release" /><summary type="html"><![CDATA[Introducing Fix 0.7, a minimalist Ruby testing framework focused on clear specifications and pure Ruby objects, built in just 148 lines of code.]]></summary></entry></feed>