So trying to upgrade some old test modules written by other people to support newer perls. Some of the tests are using Test::MockObject::Extends, but I've found running the following code errors out.
#!/usr/bin/env perl
package MyModule;
use strict;
use warnings;
use fields qw(field1 field2);
sub new {
my $self = shift;
unless (ref $self) {
$self = fields::new($self);
}
return $self;
}
package main;
use strict;
use warnings;
use Test::MockObject::Extends;
use Data::Dumper;
my $VAR1 = MyModule->new();
print Data::Dumper::Dumper($VAR1);
my $VAR2 = Test::MockObject::Extends->new($VAR1);
Error:
$ perl a
$VAR1 = bless( {}, 'MyModule' );
Modification of a read-only value attempted at /usr/local/share/perl/5.14.2/Test/MockObject/Extends.pm line 31.
I've looked at the changelog for Test::MockObject and perl 5.10 and can't see anything that directly looks like it causes this. I suspect its been broken for a while and something new for 5.10 just illuminated it.
I think what's happening here is a result of using fields::new. From the perldoc page:
perl 5.9.0 and higher: fields::new() creates and blesses a
restricted-hash comprised of the fields declared using the "fields"
pragma into the specified class.
I think Test::MockObject::Extends wants to modify the hash, hence boom.
#nfg's answer is correct, but there is a simple workaround: unlock the hash created by fields before passing it to Test::Object::Extends.
use Hash::Util qw(unlock_keys);
my $obj = Some::Class->new();
unlock_keys(%$obj);
$obj = Test::MockObject::Extends->new($obj);
This will fail if using a perl < 5.9, so if that is a concern then you could unlock the keys conditionally.
You may want to re-lock the keys after calling Test::MockObject::Extends and mocking any methods you want to intercept, because otherwise errant code that is accessing fields that should not exist in the object will not be caught.
Related
i want to write a little script to grab and play around with the results of the autocomplete of an input field.
So I find a principal solution with Selenium::Firefox, that base on module Selenium::Remote::Driver, but the description of the methods is without any examples.
I have this basic example, that is able to open google and insert a search string.
Then you can see that a result list is suggested and i want to get this list.
But i have no idea how this can be obtained?
Here is my code so far:
#!/usr/bin/perl
use strict;
use warnings;
use Selenium::Firefox;
my $mech = Selenium::Firefox->new(
startup_timeout => 20,
firefox_binary => '/srv/bin/firefox.62.0/firefox',
binary => '/usr/local/bin/geckodriver',
marionette_enabled => 1
);
my $search = "perl";
my $url = "https://www.google.com/";
$mech->get($url);
$mech->find_element_by_name("q");
sleep(3);
my $result = $mech->get_active_element();
$result->send_keys($search);
sleep (10);
$mech->shutdown_binary;
exit 0;
I could find no examples to use this Perl module - and there are more questions for it.
As for instance: find_element
How can i turn on the warnings instead of killing the script?
Or how can i step through the objects of the wep page?
Is it possible to connect to an already opened browser?
The description of the module is not understandable for people who are not experts and the authors did not answer to questions so far.
But my hope is that experts here can give me a hint?
I am trying the whole day to find out the answer, but I didn't find anything.
I wrote some tests using test::more (test1.t, test2.t, test3.t ...).
and I wrote a main perl script (main.pl) that handles all the tests using TAP::Harness and print the output in a JUnit format using formatter_class => 'TAP::Formatter::JUnit.
In my tests I use the BAIL_OUT function.
The problem is that when a test is bailed out, the main script also exits and there is no output at all. If, for example test3.t bailed_out, I need to see the results for test1.t and test2.t. how can I do that?
I can't use exit or die instead of BAIL_OUT because I don't want the other tests to continue. (If test3.t was BAIL_OUT I don't want that test4.t will run.)
can someone please help me?
I need to see the results for the tests that were running before the bailed out test.
Thanks.
According to the Test::More docs:
BAIL_OUT($reason);
Indicates to the harness that things are going so badly all testing should terminate.
This includes the running of any additional test scripts.
So that explains why your suite aborts.
You might want to consider die_on_fail from Test::Most, or skip_all, depending on the reason for the BAIL_OUT.
EDIT: Looks like Test::Builder doesn't have any intention of printing out a summary when it exits on "catastrophic failure" according to the source code:
sub BAIL_OUT {
my( $self, $reason ) = #_;
$self->{Bailed_Out} = 1;
$self->_print("Bail out! $reason");
exit 255;
}
# Don't do an ending if we bailed out.
if( $self->{Bailed_Out} ) {
$self->is_passing(0);
return;
}
However, that Bailed_Out flag is only ever used to consider printing out summary diagnostics, and since Test::More exposes the underlying Test::Builder object, you can probably just tweak the BAIL_OUT subroutine and no set this flag. All untested, of course; YMMV.
Instead of passing in all tests to one TAP::Harness, you need to pass in one test at a time to the Harness in case of a BAIL_OUT
I haven't seen your code, so here is a sample of what I mean. Adjust to include the formatter and whatever else you need.
use TAP::Harness;
my $harness = TAP::Harness->new({ merge => 0 });
my $tests = ['t/test1.t', 't/test2.t'];
foreach my $test (#$tests) {
eval {
$harness->runtests([$test]);
}; if ($#) {
# create new harness object if the previous fails catastrophically.
$harness = TAP::Harness->new({ merge => 0 });
}
}
I am using the LWP::UserAgent,
HTML::Selector::XPath and
HTML::TreeBuilder::XPath modules to get the value of the href attribute of the first YouTube video in a set of search results.
My code so far is:
use LWP::UserAgent;
use HTML::TreeBuilder::XPath;
use HTML::Selector::XPath;
my $ua = LWP::UserAgent->new;
#my $response =..
my $html = "http://www.youtube.com/results?search_query=run+flo+rida";
my $tree = HTML::TreeBuilder::XPath->new;
my $xpath = HTML::Selector::XPath::selector_to_xpath("(//*[#id = 'search-results']/li)[1]/div[2]/h3/a/#href/");
my #nodes = $tree->findnodes($xpath);
print" $nodes[0]";
I'm not sure if my printing is incorrect of if some other syntax is wrong. As of now it prints
HTML::TreeBuilder::XPath=HASH(0x1a78250)
when I am looking for it to print
/watch?v=JP68g3SYObU
Thanks for any help!
There are a number of problems here.
You must always use strict and use warnings at the top of every Perl program. It will catch many errors that you would easily overlook, and is only polite when you are asking for help with your code. In this case it would have warned you that your XPath string contained array variable names #id and #href which you may not have intended to be interpolated into the string.
You are using HTML::Selector::XPath, which translates a CSS selector to an XPath expression. But you are supplying it an XPath expression, so it will not work and the module is not needed.
There is no need to use LWP at all, as HTML::TreeBuilder has a new_from_url constructor which will fetch the HTML page for you.
This program seems to do what you need. I have also added the URI module to derive an absolute URL from the relative one in the href attribute value.
use strict;
use warnings;
use HTML::TreeBuilder::XPath;
use URI;
my $url = "http://www.youtube.com/results?search_query=run+flo+rida";
my $tree = HTML::TreeBuilder::XPath->new_from_url($url);
my $anchor = $tree->findnodes('//ol[#id="search-results"]//h3[#class="yt-lockup2-title"]/a/#href');
my $href = URI->new_abs($anchor->[0]->getValue, $url);
print $href;
output
http://www.youtube.com/watch?v=JP68g3SYObU
I'm brand new to Moose. Up until today our environments have been on Perl 5.8.2 which would not support Moose.
I'm working through some examples, and I thought that the "required => 1" setting on an attribute would be handy, however when I try using that option, the error message that is returned is not really usable.
Here's an example:
cat.pl:
#!/usr/bin/perl
{
package Cat;
use Moose;
use Modern::Perl;
has 'name' => (
is => 'ro',
required => 1,
);
sub meow {
my $self = shift;
say 'Meow!';
}
}
use Modern::Perl;
my $alarm = Cat->new();
$alarm->meow();
$alarm->meow();
$alarm->meow();
When I run it:
Attribute (name) is required at /app/perl5/perl-5.10.1/lib/site_perl/5.10.1/aix-thread-multi-64all/Class/MOP/Class.pm line 581
Class::MOP::Class::_construct_instance('Moose::Meta::Class=HASH(0x110ac1a00)', 'HASH(0x110c3b3c0)') called at /app/perl5/perl-5.10.1/lib/site_perl/5.10.1/aix-thread-multi-64all/Class/MOP/Class.pm line 554
Class::MOP::Class::new_object('Moose::Meta::Class=HASH(0x110ac1a00)', 'HASH(0x110c3b3c0)') called at /app/perl5/perl-5.10.1/lib/site_perl/5.10.1/aix-thread-multi-64all/Moose/Meta/Class.pm line 258
Moose::Meta::Class::new_object('Moose::Meta::Class=HASH(0x110ac1a00)', 'HASH(0x110c3b3c0)') called at /app/perl5/perl-5.10.1/lib/site_perl/5.10.1/aix-thread-multi-64all/Moose/Object.pm line 28
Moose::Object::new('Cat') called at cat.pl line 20
If one of our non-perl operators see an error message like that, they will probably freak out. I'm afraid they will not realize that all 5 lines in the error message are actually a part of the same error.
Is there a way to get a nice error message if a required attribute is not supplied?
Something like croak, I can imagine a message like this:
Attribute (name) is required at cat.pl line 20
Again, I'm new to Moose so this may be an easy setting that I am missing.
Thanks in advance!
I think I may have found a solution to my requirement, but I'm not sure if it is the best solution.
And, as #Tanktalus points out, there is value to having a detailed error message.
For the purposes of my question, the MooseX::Constructor::AllErrors extension seems to work:
#!/usr/bin/perl
{
package Cat;
use Moose;
use MooseX::Constructor::AllErrors;
use Modern::Perl;
has 'name' => (
is => 'ro',
required => 1,
);
sub meow {
my $self = shift;
say 'Meow!';
}
}
use Modern::Perl;
my $alarm = Cat->new();
$alarm->meow();
$alarm->meow();
$alarm->meow();
On running, I get:
Attribute (name) is required at cat.pl line 21
Which is what I was thinking.
Since I am not familiar at all with Moose, is this extension okay to use, or will it "muffle" all of the error messages?
I prefer the long error message - because if I'm missing a required parameter, I want my non-perl users to freak out: I obviously missed something in development, unit-test, and system test if it gets all the way to a user with this type of message.
Mind you, I also leave fatal warnings turned on when I go to production. I prefer my failures to be spectacular so that I can't accidentally ignore them.
Since "name" is required, what you need to do is populate this attribute from the constructor. Like this:
my $alarm = Cat->new({'name' => 'Sylvester'});
This should fix your problem.
I'm having some trouble getting filters working with HTTP::Proxy and I just can't seem to figure out what I should add to the logmask() function to get that information.
I've got a log file, that part is fine, logging is happening, but no information about filters, although they're implemented and (sometimes) working.
I've tried
logmask(['FILTERS'])
logmask('FILTERS')
logmask(FILTERS)
and none of those work! What am I missing?
Also, what's all that about the powers of two for the mask? And the constants being exported by :log?
I'm rather confused, as you can tell.
EDIT:
going by the advice below, I have the following script:
#!/sw/bin/perl
use strict;
use warnings;
use HTTP::Proxy qw( :log );
use HTTP::Proxy::BodyFilter::tags;
use HTTP::Proxy::BodyFilter::simple;
open( LOG, '>>', "/Users/ambrose/proxy-log.txt" ) or die "$!";
my $proxy = HTTP::Proxy->new;
$proxy->port(3128);
$proxy->logfh(*LOG);
$proxy->logmask( ALL );
$proxy->push_filter(
mime => 'text/html',
response => HTTP::Proxy::BodyFilter::tags->new(),
response => HTTP::Proxy::BodyFilter::simple->new(
sub { ${ $_[1] } =~ s!(</?)i>!$1b>!ig }
)
);
$proxy->start;
which doesn't log anything about filters, although the filter is in operation, I can see that italics have been changed to bold, as in the example.
If I change the line:
$proxy->logmask( ALL );
to
$proxy->logmask( FILTERS );
nothing gets added to the log file at all.
Note that the logging constants are not exported by default, but by the :log tag. They can also be exported one-by-one.
They're symbolic constants, so you want to do $proxy->logmask( FILTERS ), but first you need to use HTTP::Proxy qw(:log) or use HTTP::Proxy qw(FILTERS).
Or if you don't like namespace pollution you should be able to do $proxy->logmask( HTTP::Proxy::FILTERS() )