Techioz Blog

RSpec: 含まれているモジュールではメソッドのみが利用可能になり、定数は利用できないのはなぜですか?

概要

テスト スイート全体の単体テストで使用したい定数を含むモジュールがあります。

私が理解できないのは、モジュールを含めると定数が使用できないのに、メソッド (モジュールに追加した場合) が使用できる理由です。

例:

module Namespace
  module Constants
    K = 1
    def mymethod; end
  end
end

describe Namespace::Subject do
  include Namespace::Constants
  context "Context" do
    it "should execute" do
      mymethod # succeeds
      K        # fails
    end
  end
end

同じ原則がコンソールでも機能します。

2.5.5 :008 > include Namespace::Constants
 => Object 
2.5.5 :010 > K
 => 1 

解決策

名前空間内で定義された定数は、名前空間内から定数を先頭に置くことで呼び出すことができます。継承は、それが定義されているレベルで行われます。新しいモジュールまたはクラスにメソッドを含める場合、それらのメソッドは、定義されているオブジェクト レベルで呼び出すことができます。

K が Ruby コンソール内で機能したのは、それらをデフォルトまたはグローバル スコープに含めたためです。たとえばこれを実行した場合は、異なる結果が得られるでしょう。

class A
  include Namespace::Constants
end

K
NameError: uninitialized constant K
A::K
=>1

a = A.new

a.class::K
=>1

これも説明できる仕様です

require 'pry'

module Namespace
  module Constants
    def mymethod; end
    K = 1
  end
end

module Namespace
  module Subject
    include Namespace::Constants
  end
end

class A
  include Namespace::Constants
end

describe Namespace::Subject do
  describe 'Inherited Constants' do
    it 'is defined module level' do
      # Check if K is defined at the class level
      expect(Namespace::Subject.const_get(:K)).to eq(1)
      expect(Namespace::Subject::K).to eq(1)
    end
  end
end

describe A do
  describe 'instance methods' do
    it 'defines mymethod' do
      a = A.new
      expect(a).to respond_to(:mymethod)
    end
  end
  describe 'inherits' do
    it 'inherits class definitions' do
      expect(A::K).to eq(1)
    end
  end
end

describe 'Global Scope'  do
  it 'K is not defined' do
    include Namespace::Constants
    begin
      K
    rescue NameError => e
      puts e
    end
  end
end

これが Ruby での継承の仕組みを説明できれば幸いです。あなたが直面している問題は、Rspec 自体とは何の関係もありません。