🌊

RSpec の described_class を試してみた

2024/11/22に公開

RSpec の described_class は普段使わないんですがいろいろと試してみました。

described_class とは

described_class とはその名の通り describe に渡したクラスを返すメソッドになります。

require "rspec"

describe Array do
  it do
    pp described_class
    # => Array
  end
end

単に describe に渡した値が返ってくるだけなのかな?と思ったらそうでもなかった。

モジュールを渡した場合

described_class という名前なんですがモジュールを渡した場合には module が返ってきます。

require "rspec"

describe Enumerable do
  it do
    pp described_class
    # => Enumerable
  end
end

文字列を渡した場合

文字列を渡した場合は nil が返ってくるみたいですね。

require "rspec"

describe "String" do
  it do
    pp described_class
    # => nil
  end
end

class / module 以外だと nil を返すんですかね…?

数値や配列を渡した場合

と思ったんですが数値や配列の場合はその値を返してきてたので文字列だけが特別なのかも?

require "rspec"

describe 42 do
  it do
    pp described_class
    # => 42
  end
end

describe [1, 2, 3] do
  it do
    pp described_class
    # => [1, 2, 3]
  end
end

describe :Symbol do
  it do
    pp described_class
    # => :Symbol
  end
end

described_class は何を返している?

described_class は内部で self.class.metadata[:described_class] の値を返しているみたい。

require "rspec"

describe Array do
  it do
    pp self.class.metadata
    # => {block: #<Proc:0x000073d2cc9efad0 /tmp/vE0nmCZ/63:3>,
    #     description_args: [Array],
    #     description: "Array",
    #     full_description: "Array",
    #     described_class: Array,
    #     file_path: "/tmp/vE0nmCZ/63",
    #     line_number: 3,
    #     location: "/tmp/vE0nmCZ/63:3",
    #     absolute_file_path: "/tmp/vE0nmCZ/63",
    #     rerun_file_path: "/tmp/vE0nmCZ/63",
    #     scoped_id: "1"}
  end
end

じゃあ described_class の値はどう取得してリウのかというと https://github.com/rspec/rspec-core/blob/aec5f49485d97908183dbe790a7fefb8baaa8091/lib/rspec/core/metadata.rb#L304-L309 のあたりで取得しているみたいですかね?

def described_class
  candidate = metadata[:description_args].first
  return candidate unless NilClass === candidate || String === candidate
  parent_group = metadata[:parent_example_group]
  parent_group && parent_group[:described_class]
end

NilClassString のときのみだけ特殊な動きになってそう。

describe がネストしている場合はどうなるのか

describe ネストしている場合は内側の値を返します。

require "rspec"

describe Array do
  describe String do
    it do
      pp described_class
      # => String
    end
  end
end

ただし、文字列の場合は nil ではなくてその外側の describe を返すので注意してください。

require "rspec"

describe Array do
  describe "String" do
    it do
      pp described_class
      # => Array
    end
  end
end

多分 describe がネストするときは describe "#hoge" みたいにメソッド名などを提議することが多いのでその対応として String が特別になっているんですかねー。

require "rspec"

describe Array do
  describe "#size" do
    it do
      # この場合は外のクラスを参照するようにしている
      pp described_class
      # => Array
    end
  end
end
GitHubで編集を提案

Discussion