Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでのネットワークサービスのテストは思ったより簡単です

新しいプロジェクトを開始し、コードをサードパーティのサービスに依存するときが来ました。これは、ElasticSearch、Resque、課金プロバイダー、または任意のHTTPAPIのようなものである可能性があります。あなたは優れた開発者なので、このコードを十分にテストする必要があります。しかし、完全に制御できないサービスに対してオフリクエストを発生させるコードをどのようにテストしますか?

テストをスキップすることもできますが、まもなく不安定な基盤にさらに多くのコードを積み上げることになります。テストされていないコードは、より複雑なコードを引き付ける傾向があり、安全に感じる必要のあるテストカバレッジがないため、最終的にはコードが危険すぎてリファクタリングできないように感じます。将来の作業のために安定した基盤を構築したいのですが、その代わりに、保守不可能な混乱に陥っています。

この状況を回避することは、見た目よりもはるかに簡単です。いくつかのツールと少しの事前の努力で、コードが依存するサービスからテストを切り離し、より単純なコードを記述し、バグを導入することなく、記述したコードを改善する自信を持つことができます。次のテストの作成方法がわからないために先延ばしにする代わりに、コードと外界との相互作用を調べて、コードの真ん中に飛び込む方法を正確に知ることができます。

モカ:迅速で汚いアプローチ

モカは、コードと外の世界との間を行き来する最も簡単な方法です。

例として、Cartがあるとします。 チェックアウト時にクレジットカードの請求をトリガーするオブジェクト。充電に失敗した場合は、カートにエラーメッセージが添付されていることを確認する必要があります。

テストを実際にヒットしたくない場合があります。 テストが実行されるたびに課金システム。たとえそうしたとしても、そのサービスに障害を返すように強制するのは難しいかもしれません。モカの場合は次のようになります:

def test_error_message_set_on_charge_failure
  cart = Cart.new(items)
  cart.stubs(:charge!).returns(false) # mocha in action
  cart.checkout!
  assert_equal "The credit card could not be charged", cart.credit_card_error
end

メソッドが期待どおりに呼び出されない場合、Mochaはテストに失敗する可能性もあります。

def test_only_bill_once_per_cart
  cart = Cart.new(items)
  cart.expects(:charge!).once # Don't double-bill, no matter how many times we check out
  cart.checkout!
  cart.checkout!
end

モカは使い方は簡単ですが、信じられないほど便利です。 のみをモックアウトしていることに注意する必要があります 起こりたくない行動–嘲笑しすぎて、実際のバグを隠すのは簡単です。また、このアプローチをやりすぎたくない場合もあります。テストはexpectsでいっぱいです。 およびstubs 読んだり考えたりするのは難しいです。

偽物のテスト:私の好みのアプローチ

同じオブジェクトで常に同じメソッドをモックまたはスタブ化する場合は、モックを本格的なオブジェクトに昇格させることができます(テストフェイクと呼ばれることもあります)。 )、このように:

def test_billed_full_amount_minus_discount
  test_payment_provider = TestPaymentProvider.new # A fake payment provider
  cart = Cart.new(items, discount: 30, provider: test_payment_provider)
  cart.checkout!

  assert_equal items.sum(:&price) * 0.7, test_payment_provider.total_charges
end

偽物は素晴らしいです:

  • あなたの偽物はその内部状態を追跡することができます

    偽物には、total_chargesのように、テストの記述を容易にするカスタムアサーションメッセージとヘルパー関数を含めることができます。 上記の例の方法。

  • 本格的なオブジェクトとして、追加のエディターと言語のサポートを利用できます

    それをサポートするエディターを使用している場合は、Mochaを使用して個々のメソッドをスタブ化することでは得られない、autocomplete、インラインドキュメント、およびその他のものを取得できます。また、より優れた検証、例外処理、および偽物に組み込みたいその他の機能も利用できます。

  • 開発モードで偽物を使用する場合は、実際のサービスに接続する必要はありません

    バス上でアプリを作成できます。ラップトップのバッテリーを実行するサービスのフォレストを用意する必要はありません。また、これらの偽のサービスを設定して、多くのことを必要とせずに、エッジケースを処理するために必要なデータを返すことができます。セットアップ。

  • これらのオブジェクトはテスト外で使用できます

    これはおそらく偽物の私のお気に入りの部分です。メモリ内の配列に裏打ちされた、サードパーティのサービスと偽物の両方へのloggingclientログを持つことができます。 この配列の内容をサイトの管理者ビューにダンプできます 、ログに記録していると思うものをログに記録していることを確認するのがはるかに簡単になります。

次のようなことができます:

  fake_backend = FakeBackend.new
  LoggingService.backends = [RealBackend.new, fake_backend]
  LoggingService.debug("TEST MESSAGE PLEASE IGNORE")
  fake_backend.messages.first # => [:debug, "TEST MESSAGE PLEASE IGNORE"]

偽物を書くことは、個々のメソッドをスタブするよりも手間がかかりますが、実際には、役立つ偽物を作成するのに1、2時間以上かかることはありません。他の人に役立つものを作成する場合は、それを共有してください!私は昔からレスクユニットを作りましたが、今でも多くの人が使っています。

とにかく、これらのオブジェクトを注入するにはどうすればよいですか?

これらの偽物と話すには、オブジェクトをテストする必要があります。幸いなことに、Rubyは悪用されやすいため、偽物を注入することは難しくありません。

テスト対象のオブジェクトのAPIを制御する場合は、デフォルトのパラメータ、属性、または偽物を設定できるコンストラクタオプションを追加することをお勧めします。

class Card
  attr_reader :provider
  def initialize(items, options={})
    @provider = options.fetch(:provider) { RealProvider.new }
  end
end

これは、実際のサービスと話しているときにクリーンであり、後で柔軟性を追加するためのフックを提供します。

オブジェクトを制御しない場合、または追加のパラメーターを追加したくない場合は、いつでもモンキーパッチを適用できます:

# if in test mode
Card.class_eval do
  def provider
    @provider ||= TestProvider.new
  end
end

テストでは醜いですが、偽物を使用しない環境ではよりクリーンです。

今すぐ自分の偽物の作成を開始

偽物の作成は練習することで簡単になるので、試してみる必要があります:

  • 外部サービスと通信するテストを見つけます。インターネットから切断した場合に失敗するテストは、適切な候補です。
  • 実際に通信を行うオブジェクトと、そのオブジェクトに対してコードが呼び出す呼び出しを把握します。
  • そのオブジェクトのクラスのほとんど空の複製を作成し、配列に対して行った呼び出しをログに記録させます。
  • 偽物にメソッドを追加して、行われた呼び出しのリストを返します。
  • 実際のオブジェクトを新しい偽のオブジェクトと交換し、コードが行う呼び出しに対していくつかのアサーションを記述します。

試してみたら、どうなるか教えてください!

これらの手法を使用すると、アプリケーションと外界との間の最もクレイジーな相互作用を飼いならすことができるようになるまで、長くはかかりません。適切な場所にあるシンプルなスタブを使用すると、十分にテストされたコードを自信を持って出荷できます。


  1. Rubyでの静的分析

    ソースコードを解析して、すべてのメソッド、それらが定義されている場所、およびそれらが取る引数を見つけたいとします。 どうすればこれができますか? あなたの最初のアイデアはそれのために正規表現を書くことかもしれません… しかし、もっと良い方法はありますか? はい! 静的分析 は、ソースコード自体から情報を抽出する必要がある場合に使用できる手法です。 これは、ソースコードをトークンに変換する(解析する)ことによって行われます。 さっそく始めましょう! パーサージェムの使用 Rubyには標準ライブラリで利用可能なパーサーがあります。名前はRipperです。出力を操作するのは難し

  2. Rubyネットワークプログラミング

    Rubyでカスタムネットワーククライアントとサーバーを作成しますか?または、それがどのように機能するかを理解しますか? 次に、ソケットを処理する必要があります。 このルビーネットワークプログラミングのツアーに参加してください 基本を学び、Rubyを使用して他のサーバーやクライアントと会話を始めましょう! では、ソケットとは何ですか ? ソケットは通信チャネルのエンドポイントであり、クライアントとサーバーの両方がソケットを使用して通信します。 動作方法は非常にシンプルです : 接続が確立されると、データをソケットに入れることができます。データはもう一方の端に送られ、そこで受信者はソケ