Rubyでの依存性注入に特化したHabr に関する記事は既にありましたが、より重要なのは、 dry-containerおよびdry-auto_inject gemを使用したIoC-containerパターンの使用です。 しかし、依存性注入を利用するために、コンテナーをフェンスしたり、ライブラリーを接続したりする必要はまったくありません。 今日は、自分の手で DIをすばやく実装する方法について説明します 。
アプローチの説明
人々は何のためにDIを使用しますか? 通常、テスト中にコードの動作を変更するため、外部サービスへの呼び出しを回避するため、または単に環境から隔離してオブジェクトをテストするためです。 もちろん、DHHはTime.now
を停止し 、不必要なジェスチャーなしでテストの緑色のポイントを楽しむことができると言いますが、DHHが言うことすべてを盲目的に信じないでください。 個人的には、 この投稿で紹介したピョートル・ソルニカの視点が好きです。 彼は例を挙げます:
class Hacker def self.build(layout = 'us') new(Keyboard.new(layout: layout)) end def initialize(keyboard) @keyboard = keyboard end # stuff end
コンストラクターのkeyboard
パラメーターは、依存性注入です。 このアプローチにより、 Hacker
クラスをテストして、実際のKeyboard
インスタンスの代わりにmokiを渡すことができます。 分離、すべてのもの:
describe Hacker do let(:keyboard) { mock('keyboard') } it 'writes awesome ruby code' do hacker = Hacker.new(keyboard) # some expectations end end
しかし、上記の例で気に入っているのは、キーボードが初期化される.build
メソッドのエレガントなトリックです。 DIの議論で、コントローラーなどの呼び出しコードへの依存関係を初期化することを提案する多くのヒントを見ました。 ええ、それからハッカーの発生をプロジェクト全体で検索して、どの特定のクラスがキーボードに使用されているかを確認します。 .build
business:目立つ場所でのデフォルトのユースケースであるかどうかにかかわらず、何も探す必要はありません。
発信者テスト
次の例を考えてみましょう。
class ExternalService def self.build options = Config.connector_options new(ExternalServiceConnector.new(options)) end def initialize(connector) @connector = connector end def accounts @connector.do_some_api_call end end class SomeController def index authorize! ExternalService.build.accounts end end
コントローラは、実際のオブジェクトを使用してExternalServiceを作成することがわかります(ただし、これはExternalService.build
メソッドに隠されています)。これは、DIを実装することで回避しようとします。 この状況に対処する方法は?
- 呼び出しコードをまったくテストしないでください。 まあまあのオプション、私はそれを書き留めて絵を完成させることにしました。
ExternalService.build
置き換えます。 実際、DHHが話していたことは重要ですが、重要なポイントが1つあります.build
を置き換えることは、クラスインスタンスの動作を変更せず、ラッパーのみを変更することです。 RSpecの例:
connector = instance_double(ExternalServiceConnector, do_some_api_call: []) allow(ExternalService).to receive(:build) { ExternalService.new(connector) }
- CIサーバーで統合テストを使用してコントローラーをテストします。 長所:実動コードがテストされ、潜在的なユーザーがユーザーではなく潜在的なバグをキャッチする可能性が高まります。 短所:例外的な状況(「サードパーティのサービスがクラッシュした」)をテストすることはより難しく、サードパーティのサービスには、テストを安全に実行できるサンドボックスアカウントが常にあるとは限りません。
- IoCコンテナーを使用します。
2番目と3番目のアプローチの組み合わせが最も効果的だと思われます:2番目のアプローチを使用して、例外的な状況をテストします.3番目のアプローチを使用して、オブジェクトをインスタンス化するコードにエラーがないことを確認します。
結論
上記にもかかわらず、私は一般にIoCコンテナの使用に反対ではありません。 選択肢があることを覚えておくと便利です。
投稿で使用されるリンク: