7

最適化したい低速仕様がいくつかあります。そのような仕様の例は次のようになります。

require 'rspec'


class HeavyComputation
  def compute_result
    sleep 1 # something compute heavy here
    "very big string"
  end

end



describe HeavyComputation, 'preferred style, but slow' do

  subject { described_class.new.compute_result }

  it { should include 'big' }
  it { should match 'string' }
  it { should match /very/ }
  # +50 others
end

これは非常に読みやすく、一般的には満足しています。ただし、仕様を追加するたびに合計実行時間が少なくとも 1 秒長くなります。それはあまり受け入れられません。

HeavyComputation(この質問の範囲外であるため、クラスの最適化については議論しないでください)。

したがって、私が頼らなければならないのは、次のような仕様です。

describe HeavyComputation, 'faster, but ugly' do
  subject { described_class.new.compute_result }

  it 'should have expected result overall' do
    should include 'big'
    should match 'string'
    should match /very/
    # +50 others
  end
end

実行時間は常にほぼ一定であるため、これは明らかにパフォーマンス面ではるかに優れています。問題は、障害を追跡するのが非常に難しく、直感的に読み取ることができないことです。

理想的には、両方を混在させたいと思います。これらの行に沿ったもの:

describe HeavyComputation, 'what I want ideally' do
  with_shared_setup_or_subject_or_something_similar_with do
    shared(:result) { described_class.new.compute_result  }
    subject         { result }

    it { should include 'big' }
    it { should match 'string' }
    it { should match /very/ }
    # +50 others
  end
end

しかし、残念ながら、どこから実装を開始すればよいかわかりません。これには複数の潜在的な問題があります (共有結果でフックを呼び出す必要があります)。

この問題に対する既存の解​​決策があるかどうか知りたいこと。いいえの場合、それに取り組むための最良の方法は何ですか?

4

3 に答える 3

4

before(:context)フックを使用してこれを実現できます。

describe HeavyComputation, 'what I want ideally' do
  before(:context) { @result = described_class.new.compute_result }
  subject          { @result }

  it { should include 'big' }
  it { should match 'string' }
  it { should match /very/ }
  # +50 others
end

before(:context)ただし、いくつかの注意事項があることに注意してください。

警告:before(:context)

物事をスピードアップするために使用するのは非常に魅力的before(:context)ですが、多くの落とし穴や単に機能しないものがあるため、これを避けることをお勧めします.

環境

before(:context)ブロックのグループ コンテキストを提供するために生成される例で実行されます。

インスタンス変数

で宣言されたインスタンス変数はbefore(:context)、グループ内のすべての例で共有されます。これは、各例が共有オブジェクトの状態を変更する可能性があることを意味します。その結果、順序依存性が生じ、失敗についての推論が困難になる可能性があります。

サポートされていない rspec 構造

RSpec には、各例の間で状態を自動的にリセットするいくつかの構造があります。これらは 内からの使用を意図していませんbefore(:context):

  • let宣言
  • subject宣言
  • モッキング、スタブ、またはテストの double 宣言

他のフレームワーク

モック オブジェクト フレームワークとデータベース トランザクション マネージャー (ActiveRecord など) は、通常、例の前にセットアップし、その 1 つの例を実行してから破棄するという考えに基づいて設計されています。これは、モックとスタブを (場合によっては) で宣言できることを意味しますが before(:context)、最初の実際の例が実行される前に取り壊されます。

rspec-rails でデータベースに基づくモデル オブジェクトを作成できますbefore(:context)が、トランザクションにラップされないため、自分でafter(:context)ブロックをクリーンアップする必要があります。

( http://rubydoc.info/gems/rspec-core/RSpec/Core/Hooks:beforeより)

before(:context)フックが、テスト ダブルや DB トランザクションなどの例ごとの通常のライフサイクルの範囲外であることを理解し、必要なセットアップとティアダウンを自分で明示的に管理する限り、問題はありませんが、コード ベースで作業する他の人は、これらの落とし穴に気付いていない可能性があります。

于 2014-09-03T02:46:38.357 に答える
1

@Myron Marstonはいくつかのインスピレーションを与えたので、多かれ少なかれ再利用可能な方法でそれを実装する最初の試みは、次の使用法で終わりました( に注意してくださいshared_subject):

describe HeavyComputation do
  shared_subject { described_class.new.compute_result }

  it { should include 'big' }
  it { should match 'string' }
  it { should match /very/ }
  # +50 others
end

アイデアは、共有ブロックではなく、最初のスペックで、対象を一度だけレンダリングすることです。これにより、何も変更する必要がほとんどなくなります (すべてのフックが実行されるため)。

もちろんshared_subject、すべての癖のある共有状態を想定しています。

ただし、新しくネストされるたびcontextに新しい共有サブジェクトが作成され、状態リークの可能性がある程度排除されます。

さらに重要なことは、状態リーク s に対処するために必要なことは、 s を元にshared_subject戻すことだけsubjectです。次に、通常の RSpec の例を実行します。

実装にはいくつかの癖があると確信していますが、かなり良いスタートになるはずです。

于 2014-09-23T02:43:50.133 に答える
1

aggregate_failuresバージョン 3.3 で追加された は、あなたが求めていることの一部を実行します。仕様内に複数の期待値を持つことができ、RSpec は最初の期待値で停止するのではなく、それぞれを実行してすべての失敗を報告します。

問題は、単一の仕様内に配置する必要があるため、それぞれの期待に名前を付けることができないことです。

ブロックフォームがあります:

it 'succeeds' do
  aggregate_failures "testing response" do
    expect(response.status).to eq(200)
    expect(response.body).to eq('{"msg":"success"}')
  end
end

そして、仕様全体に適用されるメタデータ形式:

it 'succeeds', :aggregate_failures do
  expect(response.status).to eq(200)
  expect(response.body).to eq('{"msg":"success"}')
end

参照: https://www.relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures

于 2016-05-20T00:16:42.623 に答える