What Ruby code to expect from a testing DSL?

Have you ever wondered what the Ruby code generated by your test DSL might look like?

As a developer, it might be interesting to know. Maybe it's so horrible that it's impossible to write directly. Or maybe it's even lighter than a minitest test set. Who knows?

To try to answer this question while avoiding the endless debate between RSpec and minitest, I will use the RSpec clone project for the purpose of this post.

What is RSpec clone?

It is a minimalist reimplementation of RSpec that emphasizes correctness, security and standardization.

Driven by the desire to enforce the guidelines and best practices outlined in the community RSpec style guide, this RSpec clone includes most of RSpec’s DSL to express expected outcomes with no magical powers.

Without further ado, here are some correspondences between the DSL syntax and the generated Ruby code to understand how the framework builds and executes tests. For ease of reading, these mappings are grouped by method.

From DSL to Ruby

describe method

When I write this,

RSpec.describe String do
end

I expect this code to be generated:

class AnonymousClass
  private

  def described_class
    String
  end
end

context method

When I write this,

RSpec.context "when in a context" do
end

I expect this code to be generated:

class AnonymousClass
end

subject method

When I write this,

RSpec.describe ".subject" do
  subject do
    "foo"
  end
end

I expect this code to be generated:

class AnonymousClass
  private

  def subject
    "foo"
  end
end

Embedded describe method

When I write this,

RSpec.describe ".describe" do
  describe "embedded describe" do
  end
end

I expect this code to be generated:

class AnonymousClass1
end

class AnonymousClass2 < AnonymousClass1
end

let method

When I write this,

RSpec.describe ".let" do
  let(:answer_to_everything) { 41 + one }
  let(:one) { 1 }
end

I expect this code to be generated:

class AnonymousClass
  private

  def answer_to_everything
    41 + one
  end

  def one
    1
  end
end

before method

When I write this,

RSpec.describe ".before" do
  before do
    puts "hello"
  end
end

I expect this code to be generated:

class AnonymousClass
  private

  def initialize
    puts "hello"
  end
end

expect method

When I write this,

RSpec.describe "#expect" do
  it { expect(41.next).to be(42) }
end

I expect this code to be generated:

class AnonymousClass
end

require "matchi/rspec"
require "r_spec/clone/expectation_target"

example = Class.new(AnonymousClass) { include Matchi::Helper }
sandbox = example.new
sandbox.instance_eval { ExpectationTarget::Value.new(41.next).to be(42) }
Success: expected to be 42.

Happy testing with Ruby

Unless you don't write tests, the advantage of using such DSL interface is to limit the introduction of additional logic in to the specification document with potential errors, to encourage the use of good Ruby patterns with low algorithmic complexity, and to make Ruby code shorter and more (machine) readable.

Rspec is a testing DSL. MiniTest is ruby.

I love both, and I hope you'll like RSpec clone too.

22