我们使用 Perl 进行 GUI 测试自动化。它非常成功。我们为 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()")

这不太可读。相反,我想为应用程序、视图和页面编写 Perl DSL。Perl 是否提供某种 DSL 结构,我可以在其中执行以下操作?

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

其中 App 应是 Application 类的实例。我必须在运行时获取 View2 的对象。

这样的东西怎么用呢?

有帮助吗?

解决方案

您几乎可以用 Perl 做任何事情。但你必须做一些 奇怪的 让 Perl 使用非 Perl 的语法来执行的东西。

  • 为了准确地处理你所拥有的东西,你将不得不做很多 先进的 技巧,根据定义,这些技巧是不可维护的。你必须:

    • 超载 串联操作员'。 (需要一个祝福的参考)
    • 转动 限制 关闭或创建一个 自动加载 子词允许这些裸词 - 当然,您可以为您想要使用的所有词编写子词(或使用 裸词 模块)。
    • 可能的话,创建多个包,其中包含多个 AUTOLOADs
  • 另一种方法是 源过滤器, ,我可能会投反对票 提及 这个能力。所以我不会完全推荐这种方法给那些 询问 求助。但它就在那里。源过滤器(我已经完成了我的分享)只是您可能认为自己太聪明而不利于自己的领域之一。

    不过,如果您对 Perl 作为 DSL“宿主”语言感兴趣,那么 源过滤器 并不完全是禁区。然而,将其限制在你表现出你想做的事情上, Perl6::属性 可能会立即完成您需要的大部分功能。这需要 . 并将它们翻译成 Perl 能理解的“->”。但你仍然可以采取 在源头过滤器来理解 什么是 正在幕后进行。

    我也不想离开这个主题而不建议您可以通过使用 Damian Conway 的方法来缓解生成自己的源过滤器(我建议不要这样做)所带来的很多挫败感 过滤器::简单.

  • 最简单的方法是放弃 '.' 运算符,而是期待 Perl 外观的代码。

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

    App 要么是一个包,要么是一个子包。在当前包中定义或导入,返回一个对象,该对象被祝福到带有 View2 子(可能是 AUTOLOAD sub) 返回包的名称或被祝福到包中的引用,它理解 Page2, ,然后最后的回报就会明白 Activate 或者 Click. 。(参见 面向对象教程, , 如果你需要。)

其他提示

我建议你停止尝试做怪异的“DSL”的东西,只是写的Perl类来处理你要管理的对象。我建议你考虑使用这一新的驼鹿的Perl的对象系统,虽然传统的Perl OO会就好了。通过Perl文档为OO教程挖;它们是巨大的。

在方法的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。您也可以滥用自动加载,使所有的方法调用都成功(但注意那些具有特殊含义,就像销毁)。

这将让你的语法。有你的对象产生一个字符串传递给send_command不应该是困难的。

另外,我不是太熟悉,但你可能想看看穆斯。它可以具有更简单的方法来实现此目的。

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

http://search.cpan.org/dist/Devel-Declare/是现代替代源滤波器,在直接集成到perl的解析器的工作原理,并且是值得期待。

要重写'.'或使用->语法可能使用包语法(一种替代::),即创建例如App ::视图2和App ::视图2 ::第2页时视图2 /第2页获得创建包,添加一个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();
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top