3

REST サービスを介して API からデータを取得するサブがあります。コードはかなり単純ですが、API にパラメーターをポストする必要があり、SSL を使用する必要があるため、LWP::UserAgentを経由する必要があり、 LWP::Simpleを使用できません。これはその簡略版です。

sub _request {
  my ( $action, $params ) = @_;

  # User Agent fuer Requests
  my $ua = LWP::UserAgent->new;
  $ua->ssl_opts( SSL_version => 'SSLv3' );

  my $res = $ua->post( 
    $url{$params->{'_live'} ? 'live' : 'test'}, { action => $action, %$params } 
  );
  if ( $res->is_success ) {
    my $json = JSON->new;

    return $json->decode( $res->decoded_content );
  } else {
    cluck $res->status_line;
    return;
  }
}

これは、モジュール (OOp ではない) で必要な唯一の場所です$ua

今、私はこのためのテストを書きたいと思っています.いくつかの調査の結果、 Test::LWP::UserAgentを使用するのが最善であると判断されました.これは本当に有望に思えます. 残念ながら、落とし穴があります。ドキュメントでは、次のように述べています。

LWP::UserAgent 自体にはモンキー パッチが適用されていないことに注意してください。このモジュール (またはサブクラス) を使用してリクエストを送信する必要があります。そうしないと、キャッチして処理することができません。

useragent の実装を交換する一般的なメカニズムの 1 つは、遅延ビルドされた Moose 属性を使用することです。構築時にオーバーライドが提供されない場合、デフォルトは LWP::UserAgent->new(%options) になります。

ああ。明らかに、ムースのことはできません。$uaサブにa を渡すこともできません。もちろん、サブにオプションの 3 番目のパラメーターを追加することもできます$uaが、それを行うという考えは好きではありません。テスト可能にするためだけに、このような単純なコードの動作を根本的に変更するのはよくないと思います。

私が基本的にやりたいことは、次のようにテストを実行することです。

use strict;
use warnings;
use Test::LWP::UserAgent;
use Test::More;

require Foo;

Test::LWP::UserAgent->map_response( 'www.example.com',
  HTTP::Response->new( 200, 'OK', 
    [ 'Content-Type' => 'text/plain' ], 
    '[ "Hello World" ]' ) );

is_deeply(
  Foo::_request('https://www.example.com', { foo => 'bar' }),
  [ 'Hello World' ],
  'Test foo'
);

Test::LWP::UserAgent 機能を LWP::UserAgent にモンキーパッチして、私のコードが Test:: を使用するようにする方法はありますか?

4

3 に答える 3

4

_request()内で呼び出し_ua()てユーザー エージェントを収集し、テスト スクリプトでこのメソッドをオーバーライドするようにコードを変更します。そのようです:

モジュール内:

sub _request {
...
 my $ua = _ua();
...
}

sub _ua { 
 return LWP::UserAgent->new();
}

テスト スクリプト内:

...
Test::More::use_ok('Foo');

no warnings 'redefine';
*Foo::_ua = sub { 
    # return your fake user agent here
};
use warnings 'redefine';
... etc etc
于 2013-02-19T19:21:21.787 に答える
2

もちろん、サブにオプションの 3 番目のパラメーター $ua を追加することもできますが、それを行うという考えは好きではありません。テスト可能にするためだけに、このような単純なコードの動作を根本的に変更するのはよくないと思います。

これは依存性注入として知られており、完全に有効です。テストでは、クラスがさまざまな結果をモックするために使用するオブジェクトをオーバーライドできる必要があります。

オブジェクトをオーバーライドするより暗黙的な方法を好む場合は、Test::MockObjectTest::MockModuleを検討してください。LWP::UserAgent のコンストラクターをモックして代わりにテスト オブジェクトを返すか、テストするコードのより広い部分をモックして、Test::LWP::UserAgent がまったく必要ないようにすることができます。

もう 1 つの方法は、コンポーネントを分離して (単体) テストできるように、運用コードをリファクタリングすることです。応答の処理から HTTP フェッチを分割します。次に、独自の応答オブジェクトを作成して渡すことで、2 番目の部分をテストするのは非常に簡単です。

最終的に、プログラマーは上記のツールをすべて使用します。単体テストに適したものもあれば、より広範な統合テストに適したものもあります。

于 2013-02-19T16:27:05.250 に答える
0

今日の時点で、私はこの問題に対して次のアプローチをとっています。このレガシー コード1を想像してみてください。これはオブジェクト指向ではなく、リファクタリングできないため、依存性注入が容易になります。

package Foo;
use LWP::UserAgent;

sub frobnicate {
    return LWP::UserAgent->new->get('http://example.org')->decoded_content;
}

これは確かにテストが難しく、rjh の答えは的を射ています。しかし、2016 年には、2013 年に比べて利用可能なモジュールがいくつか増えました。特定の名前空間のサブを置き換えるが、現在のスコープ内にのみ保持するSub::Overrideが特に気に入っています。これは、完了後にすべてを復元する必要がないため、単体テストに最適です。

package Test::Foo;
use strict;
use warnings 'all';
use HTTP::Response;
use Sub::Override;
use Test::LWP::UserAgent;
use Test::More;

# create a rigged UA
my $rigged_ua = Test::LWP::UserAgent->new;
$rigged_ua->map_response( 
    qr/\Qexample\E/ => HTTP::Response->new( 
        '200', 
        'OK', 
        [ 'Content-Type' => 'text/plain' ], 
        'foo',
    ), 
);

# small scope for our override
{
    # make LWP return it inside our code
    my $sub = Sub::Override->new( 
        'LWP::UserAgent::new'=> sub { return $rigged_ua } 
    );
    is Foo::frobnicate(), 'foo', 'returns foo';
}

基本的に、すべてのテストケースに供給するTest::LWP::UserAgentオブジェクトを作成します。必要に応じて、リクエストでテストを実行するコード ref を指定することもできます (ここには示されていません)。次に、Sub::Override を使用して、LWP::UserAgent のコンストラクターが実際の LWP::UA を返さないようにしますが、既に準備され$rigged_uaた . 次に、テストを実行します。$sub範囲外になると、LWP::UserAgent::new復元され、他のことには干渉しません。

これらのテストは常に可能な限り最小のスコープで行うことが重要です (Perl のほとんどの場合と同様)。

これらのテストケースが多数ある場合は、各リクエストに期待される構成ハッシュを構築し、ビルド ヘルパー関数を使用して不正なユーザー エージェントを作成し、別の関数を使用して Sub を作成することは良い戦略です。 :オブジェクトをオーバーライドします。レキシカル スコープで使用すると、このアプローチは非常に強力であると同時にかなり簡潔になります。


use strict1) ここではとの欠如によって表されますuse warnings

于 2016-08-29T10:50:09.487 に答える