What Ruby code to expect from a testing DSL?
Tags: ruby, rspec, minitest, testing
Published: 2021-07-03 11:39:46

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](https://r-spec.dev/) 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](https://rspec.rubystyle.guide/), this RSpec clone includes most of RSpec’s DSL to express expected outcomes with no magical powers.

![What did you RSpec?](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fql5u6s7x1uw08qnr27.jpg)

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,

```ruby
RSpec.describe String do
end
```

I expect this code to be generated:

```ruby
Class.new do
  private

  def described_class
    String
  end
end
```

### `context` method

When I write this,

```ruby
RSpec.context "when in a context" do
end
```

I expect this code to be generated:

```ruby
Class.new do
end
```

### `subject` method

When I write this,

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

I expect this code to be generated:

```ruby
Class.new do
  private

  def subject
    "foo"
  end
end
```

### Embedded `describe` method

When I write this,

```ruby
RSpec.describe ".describe" do
  describe "embedded describe" do
  end
end
```

I expect this code to be generated:

```ruby
base = Class.new do
end

Class.new(base) do
end
```

### `let` method

When I write this,

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

I expect this code to be generated:

```ruby
Class.new do
  private

  def answer_to_everything
    41 + one
  end

  def one
    1
  end
end
```

### `before` method

When I write this,

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

I expect this code to be generated:

```ruby
Class.new do
  private
  
  def initialize
    puts "hello"
  end
end
```

### `expect` method

When I write this,

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

I expect this code to be generated:

```ruby
description = Class.new do
end

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

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

And I expect to see this result in console:

```txt
Success: expected to be 42.
```

## Happy testing with Ruby

Unless [you don't write tests](https://github.com/fixrb/brutal), 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.

[According to Adam Hawkins](https://speakerdeck.com/ahawkins/bow-before-minitest),

> Rspec is a testing DSL. MiniTest is ruby.

I love both, and I hope you'll like [RSpec clone](https://r-spec.dev/) too.