2016-07-12 6 views
0

それはこのように見えるためにブロックを取る方法のためのRubyでかなり一般的です:yieldを返すメソッドを適切に模倣するにはどうすればよいですか?

class File 
    def open(path, mode) 
    perform_some_setup 
    yield 
    ensure 
    do_some_teardown 
    end 
end 

それはこのように見える方法にもかなり慣用的です:

def frobnicate 
    File.open('/path/to/something', 'r') do |f| 
    f.grep(/foo/).first 
    end 
end 

私はのための仕様を書きたいですファイルシステムにヒットしない、この、それはファイルのうち、右の単語を引っ張るようなことを保証する:

describe 'frobnicate' do 
    it 'returns the first line containing the substring foo' do 
    File.expects(:open).yields(StringIO.new(<<EOF)) 
     not this line 
     foo bar baz 
     not this line either 
    EOF 
    expect(frobnicate).to match(/foo bar baz/) 
    end 
end 

ここで問題があることがMOCによって、あります王はFile.openへの呼び出しから、私はまたfrobnicatenilを返すことを意味するその戻り値を削除しました。しかし、File.returns('foo bar baz')のようなものをチェーンに追加すると、興味のあるコードに実際には当たらないテストに終わるでしょう。 frobnicateのブロックの内容は何かを行う可能性があり、テストは引き続き行われます。

私のfrobnicateメソッドを適切にテストするには、ファイルシステムを使用しないでください。私は特に特定のテストフレームワークには添付されていないので、あなたの答えが "あなたのためにそれを行うこの素晴らしい宝石を使用する"場合は、私はそれで大丈夫です。

答えて

1

Fileへの呼び出しをちょっと違うようにする必要があるようです。私はあなたのコードをそのまま実行している文法エラーを取得していましたので、私はRSpecのバージョンを確認していませんが、もしあなたが3.xであれば、これは仕事をします:

frobnicate_spec.rb
gem 'rspec', '~> 3.4.0' 
require 'rspec/autorun' 

RSpec.configure do |config| 
    config.mock_with :rspec 
end 

def frobnicate 
    File.open('/path/to/something', 'r') do |f| 
    f.grep(/foo/).first 
    end 
end 

RSpec.describe 'frobnicate' do 
    it 'returns the first line containing the substring foo' do 
    allow(File).to receive(:open).and_call_original 
    allow(File).to receive(:open).and_yield StringIO.new <<-EOF 
     not this line 
     foo bar baz 
     not this line either 
    EOF 
    expect(frobnicate).to match(/foo bar baz/) 
    end 
end 

ruby frobnicate_spec.rbを呼び出すので、指定されたRSpecバージョンを使用できます。

出典:RSpecのモックexpecting messagesyielding responses

+0

ああ、構文エラーについては申し訳ありません。 MWEを取得するために元のコードをたくさんカットしました。あなたの解決策がrspec-mocksを経由するように見えますが、私が書いたものはMocha経由です。残念ながら、私はあなたの解決策を使うときに '未定義のメソッドを許可する'を得ています。私はrspec 3.4.1です。 – ymbirtt

+0

Rubyのバージョンは何ですか? – flanger001

+0

無視 - 私はモカとの衝突を避けるためにテスト設定を更新しました。 – flanger001

0

私は以下のように投稿し、それを行うことができるminitest使用。私は、全体の実行可能ファイルを追加しているので、あなたはruby -Ilib:test test_file.rbでコマンドラインからそれをテストすることができます。

def frobnicate 
    found_string = nil 
    File.open('/path/to/something', 'r') do |f| 
    found_string = f.grep(/foo/).first 
    end 
    found_string 
end 

class FrabnicateTest < Minitest::Test 
    def test_it_works 
    mock_file = StringIO.new(%(
     not this line 
     foo bar baz 
     not hthis line either 
    )) 
    search_result = nil 
    File.stub(:open, nil, mock_file) do 
     search_result = frobnicate 
    end 
    assert_match(/foo bar baz/, search_result) 
    end 
end 
+0

私が本当に避けたいのは、 'frobnicate'の定義を編集していくつかのテストが実行されるようにすることです。テストはコードを提供しますが、それ以外の方法ではありません。 – ymbirtt

+0

テストの難しさは、テスト中のコードで何かがうまくいくはずであることを私たちに伝える匂いです。私にとってTDDのセールスポイントは、より良いコードを書くようにテストを促すことです。 Fileクラスの依存関係**をハードコードしないと、コードはより良くなります。あなたは私よりもminitestの作成者を信頼することができます:https://github.com/seattlerb/minitest/issues/499#issuecomment-73341651またはハードコーディングの依存関係についてインターネットで検索してください。そうすれば、コードは作業が楽に簡単になります(またテストも)。 – chipairon

関連する問題