はじめに
普段はC++とかTypeScriptのように型がある言語が好きな私。
最近業務でRubyとRailsとMySQLとRspecとかいう全部わからないし、しかもRubyって型がない。そんなものを使うことになって苦戦しつつも勉強して仕事しています、たのしい。
問題提起
突然ですが下のRspecのコードを見てみて下さい(疑似コード)
# frozen_string_literal: true
require 'spec_helper'
describe "test" do
def put_value(v)
# なにかside effectを起こす
end
subject do
put_value "before"
lambda do
put_value "after"
end
end
it { is_expected.to change { some_value }.from("before").to("after") }
end
意図としては、subject
の中で初期状態の設定を行って、変化を生じさせる操作もsubjectの中のlambdaに書くことで、before
とかlet
を排除して直線的に何が変化するのか読めるようにするというものです。
なあにこのlambdaという人は以下の記事を。
こういう書き方はありかどうか、これがこの記事の問題提起です。
評価順序の予想
-
subject
に渡したblock -
is_expected
: https://github.com/rspec/rspec-core/blob/dc898adc3f98d841a43e22cdf62ae2250266c7b6/lib/rspec/core/memoized_helpers.rb#L120-L122 expect
change
(change return object)#from
(change return object)#to
RSpec::Expectations::ExpectationTarget::InstanceMethods#to
反論
そもそもbefore使えばいいのでは
# frozen_string_literal: true
require 'spec_helper'
describe "test" do
def put_value(v)
# なにかside effectを起こす
end
subject { -> { put_value_after } }
before do
put_value "before"
end
let(:put_value_after) do
put_value "after"
end
it { is_expected.to change { some_value }.from("before").to("after") }
end
before は「最初にこれがあります」という前提条件の提示なのでbeforeは排除したくないというもの(職場の先輩の主張)。
自分はsubject
は先頭に書かれるべきだと思っているし、RubocopでRspec/LeadingSubject
を有効にしています。
すると見た目はsubject
→before
→let
なのに実際の順序はbefore
→subject
→let
となってしまい、上から下にコードを読めなくなってしまいます。
そもそもsubjectは必要か?
# frozen_string_literal: true
require 'spec_helper'
describe "test" do
def put_value(v)
# なにかside effectを起こす
end
before do
put_value "before"
end
let(:put_value_after) do
put_value "after"
end
it { expect { put_value_after }.to change { some_value }.from("before").to("after") }
end
is_expected
にしようとsubject
つかうから話がややこしいのであって、それをやめればすっきりするのでは?というもの。
結局冒頭のコードは断念してこちらを今回は採用することにしたのでした。
終わりに
自分はRuby学び始めて1カ月の初心者なのでRubyのプロたちの意見をお待ちしています。
余談
マッチャーと書くと抹茶に変換されがちな現象ありませんか?もう🍵でよくないですか?(だめです