22
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.
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.
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
When I write this,
RSpec.context "when in a context" do
end
I expect this code to be generated:
class AnonymousClass
end
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
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
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
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
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.
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