お気に入りのテストフレームワークのDSLを使用した場合、どのようなコードが生成されるかご存知ですか?
開発者としては、知っておくと面白いかもしれないね。もしかしたら、直接書くことができないほど恐ろしいものかもしれない。あるいは、minitestのテストセットよりも簡単かもしれない。
とにかく、RSpecとminitestの間に終わりのない議論を避けながら、この質問に答えようとするために、この記事の目的のためにRSpecクローンプロジェクトを使用する。
RSpec cloneとは?
このプロジェクトは、正確、セキュリティ、標準化を重視したRSpecのミニマリストな再実装だ。
コミュニティのRSpecスタイルガイドに記載されているガイドラインとベストプラクティスを実施したいという思いから、このRSpecクローンはRSpecのDSLのほとんどを含み、魔法の力を使わずに期待される結果を表現する。
早速ですが、フレームワークがどのようにテストを構築・実行するのかを理解するために、DSLの構文と生成されたRubyのコードの対応関係をご紹介する。読みやすくするために、これらの対応関係はメソッドごとにまとめられている。
DSLからRubyへ
describe
メソッド
これを書くと
RSpec.describe String do
end
このコードが生成されることを期待している:
Class.new do
private
def described_class
String
end
end
context
メソッド
これを書くと
RSpec.context "when in a context" do
end
このコードが生成されることを期待している:
Class.new do
end
subject
メソッド
これを書くと
RSpec.describe ".subject" do
subject do
"foo"
end
end
このコードが生成されることを期待している:
Class.new do
private
def subject
"foo"
end
end
エンベデッド describe
メソッド
これを書くと
RSpec.describe ".describe" do
describe "embedded describe" do
end
end
このコードが生成されることを期待している:
base = Class.new do
end
Class.new(base) do
end
let
メソッド
これを書くと
RSpec.describe ".let" do
let(:answer_to_everything) { 41 + one }
let(:one) { 1 }
end
このコードが生成されることを期待している:
Class.new do
private
def answer_to_everything
41 + one
end
def one
1
end
end
before
メソッド
これを書くと
RSpec.describe ".before" do
before do
puts "hello"
end
end
このコードが生成されることを期待している:
Class.new do
private
def initialize
puts "hello"
end
end
expect
メソッド
これを書くと
RSpec.describe "#expect" do
it { expect(41.next).to be(42) }
end
このコードが生成されることを期待している:
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) }
そして、このメッセージを期待している:
Success: expected to be 42.
Rubyでのテストが楽しい
テストを書かないのであれば別ですが、このようなDSLインターフェイスを使用する利点は、エラーの可能性がある追加のロジックを仕様書に導入することを制限し、アルゴリズムの複雑さが少ない優れたRubyパターンの使用を奨励し、Rubyコードをより短く、より(機械)可読にすることだ。
RSpecはテストのためのドメイン特化言語で、minitestはrubyだ。
どちらも好きなので、RSpecクローンも気に入っていただけると嬉しい。