Ruby
RSpec
stub

rspecで"`"(バッククォート)によるコマンドの実行をスタブする

More than 1 year has passed since last update.


例えばこんなコードがあったとして

class Directory

def initialize(path)
@path = path
end

def list
`ls -1 #{@path}`.chomp.split("\n")
end
end

listメソッドをテストしたいですが、実際にコマンドが実行できるように

ディレクトリやファイルを作るのは、ちょっと。。。なので

コマンド実行部分をスタブしたいです。


こうします

describe Directory do

subject { dir.list }

let(:dir) { described_class.new('/path/to/dir') }

before do
stdout = %w(file1 file2 file3).join("\n")
allow(dir).to receive(:`) { stdout }
end

it { is_expected.to eq(%w(file1 file2 file3)) }
end

テスト対象のDirectoryのインスタンスに対して`(バッククォート)メソッドが呼ばれたら

stdoutを返すようにします。


なぜ自分自身(テスト対象のインスタンス)にスタブするのか

`メソッドは、Kernelモジュールのメソッドで、

DirectoryクラスのスーパークラスObjectKernelモジュールをincludeしているからです。


バリエーション


  • 複数のDirectoryクラスのインスタンスをテストで使う場合

allow_any_instance_of(described_class).to receive(:`) { stdout }


  • 全てのコマンド実行をスタブしたい場合

allow_any_instance_of(Kernel).to receive(:`) { stdout }


まとめ

最初`をクラスメソッドと勘違いして、allow(Kernel).to receive(:`)としていたので

テストを実行すると普通にコマンドが実行されるという、残念なことになっていました。

こういうテストを書かないといけないケースは稀かも知れませんが

知ってると役に立つかもしれません。