例えばこんなコードがあったとして
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
クラスのスーパークラスObject
はKernel
モジュールをincludeしているからです。
バリエーション
- 複数の
Directory
クラスのインスタンスをテストで使う場合
allow_any_instance_of(described_class).to receive(:`) { stdout }
- 全てのコマンド実行をスタブしたい場合
allow_any_instance_of(Kernel).to receive(:`) { stdout }
まとめ
最初`
をクラスメソッドと勘違いして、allow(Kernel).to receive(:`)
としていたので
テストを実行すると普通にコマンドが実行されるという、残念なことになっていました。
こういうテストを書かないといけないケースは稀かも知れませんが
知ってると役に立つかもしれません。