トランザクションを確実に使用したいモデル関数があります。例えば:
class Model
def method
Model.transaction do
# do stuff
end
end
end
私の現在のアプローチは、ブロック内でメソッド呼び出しをスタブしてActiveRecord::Rollback
例外を発生させ、データベースが実際に変更されているかどうかを確認することです。ただし、これは、何らかの理由でブロック内の実装が変更された場合、テストが失敗することを意味します。
これをどのようにテストしますか?
別の視点から問題を見る必要があります。 関数がトランザクションを使用するかどうかをテストすることは、動作の観点からは役に立ちません。関数が期待どおりに動作するかどうかについての情報は提供されません。
テストする必要があるのは動作です、つまり、期待される結果は正しいです。わかりやすくするために、関数内で操作Aと操作Bを実行するとします(1つのトランザクション内で実行されます)。操作Aは、アプリでユーザーに100米ドルをクレジットします。操作Bは、ユーザーのクレジットカードから100米ドルを引き落とします。
ユーザーのクレジットカードからの引き落としが失敗するように、テストに無効な入力情報を提供する必要があります。関数呼び出し全体をexpect { ... }.not_to change(User, :balance)
でラップします。
このようにして、予想される動作をテストします。クレジットカードのデビットが失敗した場合は、ユーザーにその金額を入金しないでください。また、コードをリファクタリングするだけの場合(たとえば、トランザクションの使用を停止して手動でロールバックする場合)、テストケースの結果に影響を与えることはありません。
そうは言っても、@ luacassusが述べたように、両方の操作を個別にテストする必要があります。また、@ rb512が述べたように、ソースコードに「互換性のない」変更を加えた場合(つまり、動作を変更した場合)、テストケースが失敗するのはまさに正しいことです。
大きな落とし穴について言及する必要があります。トランザクションをテストするときは、transactional_fixturesをオフにする必要があります。これは、テストフレームワーク(Rspecなど)がテストケースをトランザクションブロックでラップするためです。実際には何もコミットされていないため、after_commitが呼び出されることはありません。 :requires_new => trueを使用しても、トランザクション内でのロールバックの期待は機能しません。代わりに、テストの実行後にトランザクションがロールバックされます。参照 http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html ネストされたトランザクション。
通常、「純粋な」rspecテストを使用して、アプリケーションのチャンク(クラスとメソッド)を分離してテストする必要があります。たとえば、次のコードがある場合:
class Model
def method
Model.transaction do
first_operation
second_operation
end
end
first_operation
とsecond_operation
は、理想的にはデータベースにアクセスせずに、別々のテストシナリオでテストする必要があります。後で、Model#method
のテストを作成して、これら2つのメソッドをモックすることができます。
次のステップでは、 https://www.relishapp.com/rspec/rspec-Rails/docs/request-specs/request-spec を使用して高レベルの統合テストを記述し、このコードがどのように機能するかを確認できます。 second_method
が失敗する場合など、さまざまな条件でデータベースに影響を与えます。
私の意見では、これは複雑なデータベースクエリを生成するコードをテストするための最も実用的なアプローチです。
まず、Model.transaction do ... end
ブロックをbegin rescue end
ブロックで囲む必要があります。
トランザクションのロールバックをチェックする唯一の方法は、例外を発生させることです。したがって、現在のアプローチはすべて良好です。あなたの懸念に関しては、実装の変更は常にそれに応じてテストケースを変更することを意味します。メソッドの実装が変わっても、変更を必要としない一般的な単体テストケースを持つことは不可能だと思います。
お役に立てば幸いです。
私も同じことをしていますが、おそらくあなたがする必要があるのは、ある仕様のモデルで「トランザクション」メソッドが呼び出されたことをテストしてから、他の別の仕様でブロックの本体をテストすることだけだと思います。ただし、現在のテストのようにトランザクションがメソッド呼び出しをラップすることは保証されませんが、そこに含まれる可能性のある他のコードはラップされません。