13

GUI テストの自動化には Perl を使用します。それは非常に成功しています。GUI テスト用に非常に軽量な DSL のような言語を作成しました。DSL はオブジェクト モデルに非常に似ています。

たとえば、ルートに Application オブジェクトがあります。アプリケーションの各プロパティ シートは、View オブジェクトです。ページの下の各ページは、Page オブジェクト自体と呼ばれます。Perl から GUI アプリケーションにコマンドを送信すると、GUI はコマンドを解釈し、コマンドに適切に応答します。コマンドを送信するには、次のようにします。

socket_object->send_command("App.View2.Page2.Activate()")
socket_object->send_command("App.View1.Page3.OKBtn.Click()")

これは非常に読みにくいです。代わりに、App、View、Page 用の Perl DSL を書きたいと思っています。Perl は、次のことができるような DSL 構造を提供していますか?

App.View2.Page2.Activate();
App.View1.Page2.Click();

App は Application クラスのインスタンスです。実行時に View2 のオブジェクトを取得する必要があります。

そのようなものをどのように使用するのですか?

4

6 に答える 6

21

Perl ではほとんど何でもできます。しかし、Perl に Perl ではない構文で実行させるには、奇妙なことをしなければなりません。

  • そこにあるものを正確に処理するには、多くの高度なトリックが必要になります。これは、定義上、それほど保守可能ではありません。次のことを行う必要があります。

    • 連結演算子 '.' をオーバーロードします。(祝福された参照が必要です)
    • 制限をオフにするか、 AUTOLOADサブルーチンを作成して、それらの裸の単語を許可します。もちろん、使用したいすべての単語のサブルーチンを作成することもできます (または、barewordsモジュールを使用します)。
    • AUTOLOAD場合によっては、複数のを使用して複数のパッケージを作成します
  • 別の方法はsource filtersです。この機能について言及するだけで、おそらく反対票を投じることができます。そのため、助けを求めている人にはこのアプローチをお勧めしません。しかし、それはそこにあります。ソース フィルター (そして私も共有しました) は、自分が利口すぎると思われる領域の 1 つにすぎません。

    それでも、Perl を DSL の「ホスト」言語として使用することに関心がある場合は、ソース フィルターが完全に立ち入り禁止というわけではありません。しかし、これをあなたがやりたいと示していることだけに限定すると、Perl6::Attributesはおそらくあなたがすぐに必要とするほとんどのことをしてくれるでしょう。を受け取り、.Perl が理解できる "->" に変換します。ただし、ソース フィルターを調べて、舞台裏で何が起こっているかを理解することできます。

    また、Damian Conway のFilter::Simpleを使用することで、独自のソース フィルターを生成することによるフラストレーションの多く (これはお勧めしません) が緩和されることを示唆せずに、このトピックを終了したくありません。

  • 最も簡単な方法は、「.」を忘れることです。演算子であり、代わりに Perl 風のコードを期待するだけです。

    App->View2->Page2->Activate(); 
    App->View1->Page2->Click();
    

    Appパッケージまたはサブのいずれかになります。View2現在のパッケージで定義されているか、パッケージの名前またはパッケージにブレスされた参照のいずれかを返すサブ (おそらくサブ) を持つパッケージにブレスされたオブジェクトを返すインポートされたものでAUTOLOAD、それは を理解Page2し、最後にそれからのリターンまたは。Activate_ Click(必要に応じて、 OO チュートリアルを参照してください。)

于 2008-12-05T06:30:48.467 に答える
6

気紛れな「DSL」をやろうとするのをやめて、管理したいオブジェクトを処理するための Perl クラスを書くことをお勧めします。これには新しい Moose Perl オブジェクト システムの使用を検討することをお勧めしますが、従来の Perl OO でも問題ありません。オブジェクト指向のチュートリアルについては、Perl のドキュメントを調べてください。彼らは偉大だ。

于 2008-12-05T15:13:14.543 に答える
4

perl5 でのメソッド呼び出しは->notを使用するため、本当に興味深いこと (たとえば、ソース フィルター) を行わない限り、 or.のように見えます。問題がなければ、通常の Perl OO を使用できます。App->View2->Page2->Activate()$App->View2->Page2->Active()

次に必要なことは、実行時にメソッドを作成することです。これは実際にはかなり単純です。

sub _new_view {
    my ($view, $view_num);

    # ...
    # ... (code to create $view object)
    # ...

    my $sym = "App::View$view_num";
    *$sym = sub { return $view }; # can also use Symbol package
}

または、メソッドが呼び出されたときにのみメソッドを作成する場合は、それが行われますAUTOLOAD。autoload を悪用して、すべてのメソッド呼び出しを成功させることもできます (ただし、DESTROY などの特別な意味を持つものには注意してください)。

これにより、構文が取得されます。オブジェクトに渡す文字列を生成させることsend_commandは、それほど難しくありません。

また、私はあまり詳しくありませんが、Mooseをチェックしてみてください。これを達成するためのより簡単な方法があるかもしれません。

于 2008-12-05T06:10:19.883 に答える
4

DSL ソース フィルタ

これが別の試みです。skiphoppy には一理ありますが、もう一度見てみると、(これまでのところ) そんなに複雑なことを求めていないことに気付きました。各コマンドを取得して、リモートサーバーにそれを実行するように指示するだけです. コマンドを理解する必要があるのはperlではなく、サーバーです。

そこで、ソース フィルターに関するいくつかの警告を削除し、簡単なフィルターを作成する方法を示すことにしました。繰り返しますが、あなたがしていることはそれほど複雑ではなく、以下の「フィルタリング」は非常に簡単です。

package RemoteAppScript;
use Filter::Simple;    # The basis of many a sane source filter
use Smart::Comments;   # treat yourself and install this if you don't have 
                       # it... or just comment it out.

# Simple test sub
sub send_command { 
    my $cmd = shift;
    print qq(Command "$cmd" sent.\n);
    return;
}

# The list of commands
my @script_list;

# The interface to Filter::Simple's method of source filters.
FILTER { 
    # Save $_, because Filter::Simple doesn't like you reading more than once.
    my $mod = $_;

    # v-- Here a Smart::Comment.
    ### $mod

    # Allow for whole-line perl style comments in the script
    $mod =~ s/^\s*#.*$//m;

    # 1. Break the package up into commands by split
    # 2. Trim the strings, if needed
    # 3. lose the entries that are just blank strings.
    @script_list 
        = grep { length } 
          map  { s/^\s+|\s+$//g; $_ } 
          split /;/, $mod
        ;
    ### @script_list

    # Replace the whole script with a command to run the steps.
    $_ = __PACKAGE__ . '::run_script();';
    # PBP.
    return;
};

# Here is the sub that performs each action.
sub run_script { 
    ### @script_list
    foreach my $command ( @script_list ) {
        #send_command( $command );
        socket_object->send_command( $command );
    }
}

1;

RemoteAppScript.pmこれをperl が見つけられる場所に保存する必要があります。(perl -MData::Dumper -e 'print Dumper( \@INC ), "\n"'場所を知る必要がある場合は試してください。)

次に、これを含む「perl」ファイルを作成できます。

use RemoteAppScript;
App.View2.Page2.Activate();
App.View1.Page2.Click();

でも

サーバーコマンドを保持するファイルを読み取れないという本当の理由はありません。FILTERそれは電話を捨てるでしょう。あなたが持っているだろう

App.View2.Page2.Activate();
App.View1.Page2.Click();

スクリプト ファイルに追加すると、perl ファイルは次のようになります。

#!/bin/perl -w 

my $script = do { 
    local $/;
    <ARGV>;
};

$script =~ s/^\s*#.*$//m;

foreach my $command ( 
    grep { length() } map  { s/^\s+|\s+$//g; $_ } split /;/, $script 
) { 
    socket_object->send_command( $command );
}

そして、次のように呼び出します。

perl run_remote_script.pl remote_app_script.ras
于 2008-12-05T23:03:46.903 に答える
1

http://search.cpan.org/dist/Devel-Declare/は、perl パーサーに直接統合する際に機能するソース フィルターの最新の代替手段であり、一見の価値があります。

于 2010-10-21T14:50:47.973 に答える
0

構文をオーバーライド'.'または使用する代わりに->、パッケージ構文 (::) を使用することもできます。つまり、View2 / Page 2 が作成されたときに App::View2 や App::View2::Page2 のようなパッケージを作成し、デリゲートするパッケージに AUTOLOAD サブを追加します。 App::View::Page または App::View メソッドに、次のようにします:

App/DSL.pm で:

package App::DSL;
use strict; 
use warnings;
# use to avoid *{"App::View::$view::method"} = \&sub and friends
use Package::Stash;

sub new_view(%);
our %views;

# use App::DSL (View1 => {attr1 => 'foo', attr2 => 'bar'}); 
sub import {
    my $class = shift;
    my %new_views = @_ or die 'No view specified';

    foreach my $view (keys %new_views) {
            my $stash = Package::Stash->new("App::View::$view");
        # In our AUTOLOAD we create a closure over the right
        # App::View object and call the right method on it
        # for this example I just used _api_\L$method as the
        # internal method name (Activate => _api_activate)
        $stash->add_package_symbol('&AUTOLOAD' =>  
            sub {  
                our $AUTOLOAD;
                my ($method) = 
                   $AUTOLOAD =~ m{App::View::\Q$view\E::(.*)};
                my $api_method = "_api_\L$method";
                die "Invalid method $method on App::View::$view"
                   unless my $view_sub = App::View->can($api_method);
                my $view_obj = $views{$view}
                    or die "Invalid View $view";
                my $sub = sub {
                        $view_obj->$view_sub();
                };
                     # add the function to the package, so that AUTOLOAD
                     # won't need to be called for this method again
                $stash->add_package_symbol("\&$method" => $sub);
                goto $sub;
            });
        $views{$view} = bless $new_views{$view}, 'App::View';
    }
}

package App::View;

# API Method App::View::ViewName::Activate;
sub _api_activate {
    my $self = shift;
    # do something with $self here, which is the view 
    # object created by App::DSL
    warn $self->{attr1};
}

1;

そしてあなたのスクリプトで:

use strict;
use warnings;
# Create App::View::View1 and App::View::View2
use App::DSL (View1 => {attr1 => 'hello'}, View2 => {attr1 => 'bye'});
App::View::View1::Activate();
App::View::View2::Activate();
App::View::View1::Activate();
于 2010-10-21T17:21:00.100 に答える