質問
GUIテストの自動化にはPerlを使用します。それは非常に成功しています。 GUIテスト用の非常に軽量なDSLのような言語を作成しました。 DSLはオブジェクトモデルに非常に似ています。
たとえば、ルートにApplicationオブジェクトがあります。アプリケーションの各プロパティシートは、Viewオブジェクトです。ページの下の各ページは、ページオブジェクト自体と呼ばれます。 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のオブジェクトを取得する必要があります。
そのようなことの使用方法
解決
Perlではほとんど何でもできます。しかし、PerlをPerlだけではない構文で実行させるには、奇妙なことをしなければなりません。
-
そこにあるものを正確に処理するには、多くの高度なトリックが必要になります。これらのトリックは、定義上、保守可能ではありません。次のことを行う必要があります。
-
別の方法は、ソースフィルター、この機能をメンションするためだけに、おそらく投票することができます。そのため、この方法は、助けを求めている人にはお勧めしません。しかし、それはそこにあります。ソースフィルター(および私は共有しました)は、あなたが自分の利益のためにあまりにも賢いと考えることができる分野の1つにすぎません。
それでも、DSLとしてPerlに興味があるなら<!> quot; host <!> quot;言語、ソースフィルターは正確ではありません立ち入り禁止で。ただし、これを実行したいことだけに限定すると、 Perl6 :: Attributes おそらく、すぐに必要なもののほとんどを実行できます。
.
を取り、<!> quot;-<!> gt; <!> quot;に変換します。 Perlが理解するだろう。ただし、ソースフィルターを見て見て、舞台裏で何が起こっているかを理解することができます。また、Damian Conwayの Filter :: Simple 。
-
最も簡単なことは、「。」を省略することです。演算子を使用し、代わりにPerlに見えるコードを期待します。
App->View2->Page2->Activate(); App->View1->Page2->Click();
App
はパッケージまたはサブのいずれかになります。現在のパッケージで定義されるか、インポートされて、View2
sub(おそらくPage2
sub)これは、Activate
を理解するパッケージの名前またはパッケージに祝福された参照のいずれかを返し、最後にClick
または<=>を理解するリターンを返します。 (必要に応じて、 OOチュートリアルを参照してください。)
他のヒント
変な<!> quot; DSL <!> quot;をやめることをやめることをお勧めします。管理したいオブジェクトを処理するPerlクラスを作成してください。これには新しいMoose Perlオブジェクトシステムの使用を検討することをお勧めしますが、従来のPerl OOは問題ありません。 OOチュートリアル用のPerlドキュメントを調べてください。彼らは素晴らしいです。
perl5のメソッド呼び出しは->
ではなく.
を使用するため、本当に興味深いこと(たとえば、ソースフィルター)を行わない限り、App->View2->Page2->Activate()
または$App->View2->Page2->Active()
のようになります。大丈夫だと仮定すると、通常のPerl OOのものを使用できます。
次は、実行時にメソッドを作成する必要があります。これは実際には非常に簡単です:
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
が実行します。オートロードを悪用して、すべてのメソッド呼び出しを成功させることもできます(ただし、DESTROYなどの特別な意味を持つものには注意してください)。
これにより、構文が取得されます。オブジェクトにsend_command
に渡す文字列を生成させることはそれほど難しくないはずです。
また、あまり詳しくありませんが、 Moose 。これを実現する簡単な方法があるかもしれません。
DSLソースフィルター
別の試みです。 skiphoppyにはポイントがありますが、再確認すると、(これまでのところ)それほど複雑なことを要求していないことに気付きました。各コマンドを実行し、リモートサーバーに実行するように指示するだけです。コマンドを理解する必要があるのは perl ではなく、サーバーです。
したがって、ソースフィルターに関する警告の一部を削除し、表示することにしました シンプルなものをどのように書くことができるか。繰り返しますが、あなたがしていることはそれほど複雑ではなく、私の<!> quot; filtering <!> quot;以下は非常に簡単です。
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;
これは、perlが見つけられるどこかにRemoteAppScript.pm
に保存する必要があります。 (場所を知る必要がある場合はperl -MData::Dumper -e 'print Dumper( \@INC ), "\n"'
を試してください。)
その後、<!> quot; perl <!> quot;を作成できます。これを持つファイル:
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
http://search.cpan.org/dist/Devel-Declare/は、perlパーサーに直接統合する際に機能するソースフィルターの最新の代替手段であり、一見の価値があります。
'.'
をオーバーライドするか->
構文を使用する代わりに、パッケージ構文(::)を使用することもできます。つまり、View2 /ページ2の作成時にApp :: View2やApp :: View2 :: Page2などのパッケージを作成し、次のようなApp :: View :: PageまたはApp :: Viewメソッドに委任するパッケージにAUTOLOADサブルーチンを追加します:
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();