LoginSignup
0
0

More than 1 year has passed since last update.

Ruby でグローバルなメソッドが書かれたファイルをグローバル空間に出さずにテストする

Last updated at Posted at 2021-06-10

何がしたいか

Ruby でグローバル空間にメソッドが書かれたファイルを、グローバル空間に出さずにテストしたい。

例えば以下のようなディレクトリ構造で、 apps/app1apps/app2 は互いに別々のアプリとして起動されるが、specは一箇所でやってしまいたいというとき。(例えば複数の AWS Lambda の lambda_function.rb を一つのプロジェクトとして管理してる時みたいなことを考えてください)

.
├── spec
│   └── apps_spec.rb
└── apps
    ├── app1
    │   └── main.rb
    └── app2
        └── main.rb

で、この main.rb はグローバルな空間にメソッドを置いている。

src/app1/main.rb
def main
  'hello, app1'
end
src/app2/main.rb
def main
  'hello, app2'
end

普通にテスト書こうとすると何が起きるか

このとき spec ファイルでふたつの main.rbrequire してしまうと、後で読み込まれた方で main メソッドが上書きされる。

だめなapps_spec.rb
require_relative '../apps/app1/main.rb' 

RSpec.describe 'app1' do
  describe 'main' do
    subject { main }
    # 実行時は app2 の main メソッドが上書きされてるので 'hello, app2' となる
    it { is_expected.to eq 'hello, app1' }
  end
end

require_relative '../apps/app2/main.rb' 

RSpec.describe 'app2' do
  describe 'main' do
    subject { main }
    it { is_expected.to eq 'hello, app2' }
  end
end

解決策: Module の中にメソッドを展開してモジュールメソッドとして実行する

main.rb にグローバル空間を汚染させずに、 main メソッドを実行してあげる必要がある。ここは無理やり Module の中に展開し、モジュールメソッドとして実行してみましょう。

module_eval
の第二引数として file_name を渡すと file_name の中で require_relative とかしててもちゃんと解決される。

ただしapp1, app2 で同名のモジュールを require してた場合上書き問題起きる。難あり。

app_executor.rb
module AppExecutor
  def self.execute(file_name, &block)
    Module.new.yield_self do |m|
      # file_name を展開して file_name のディレクトリとして eval 
      m.module_eval(open(file_name).read, file_name)
      m.module_eval {
        extend self
        yield(self)
      }
    end
  end
end

で、テストの方ではモジュールメソッドとして実行させる。

apps_spec.rb
RSpec.describe 'app1' do
  describe 'main' do
    subject { AppExecutor.execute('src/app1/main.rb') { |context| context.main } }
    it { is_expected.to eq 'hello, app1' }
  end
end

RSpec.describe 'app2' do
  describe 'main' do
    subject { AppExecutor.execute('src/app2/main.rb') { |context| context.main } }
    it { is_expected.to eq 'hello, app2' }
  end
end

かなり無理やり感はあるが動いたので良しということにする。
実行環境を取り出せるようにしたらモックとかもできるのでそっちのほうがいいかも。
ソースコードはこちら

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0