Académique Documents
Professionnel Documents
Culture Documents
Peter Edwards
peter@dragonstaff.co.uk
1 06/02/12
Contents
Background aka Cross-Platform Testing
"Real Life"
Add Windows Testing Under Unix Test::MockObject Test::MockModule Running Unix unit tests under Windows Future Plans For Testing Summary and Links
2 06/02/12
Client-side
GUI using WxPerl (WxWidgets) WYSIWYG editing Talks SOAP over HTTP to server Runs under ActiveState Perl Handles SOAP requests Stores document blobs in filesystem Stores indexes, metadata in Oracle database Runs under Solaris Perl
Server-side
Usage
100s of users Time critical publishing : failure during release is not an option
Real Life Cross-Platform Testing 3 06/02/12
Cross-Platform
CMS code running on Windows and Solaris
4 06/02/12
Testing
Unit
tests for dev Automated overnight smoke testing of unit tests Dev / Staging Test / Live environments Manual release test on staging test area using Windows app Problems Lots of tests for server side code, very few for client side because difficult to run 'use Wx' code on Unix in batch Existing tests run on Unix, fail on Windows
Real Life Cross-Platform Testing 5 06/02/12
Installation
1)
$ alias runxvfb='Xvfb :10 -dev vfb screen 0 1152x900x8 > /dev/null 2>&1 &' Lets you check code compile and call many routines But how do you test UI rendered properly - interpreting the virtual screen bitmaps is too hard! Mock required Win32 functions Make them do file I/O to a sandbox area Test::MockObject - Perl extension for emulating troublesome interfaces Test::MockModule - Override subroutines in a module for unit testing
6 06/02/12
Test::MockObject 1
Helpers sub make_mock_obj_in_class { my $class = shift; my $obj = Test::MockObject->new; $obj->fake_module($class); $obj->fake_new($class); return $obj; } sub dump_mock_calls { my $mockobj = shift; my $i = 1; while ( my $name = $mockobj->call_pos($i) ) { diag " call $i: $name"; my @args = $mockobj->call_args($i); for (0 .. $#args) { diag ' arg '.($_ +1).': '; diag Dumper($args[$_]); } $i++; } }
7 06/02/12
Test::MockObject 2
Mocking my $wx = make_mock_obj_in_class( 'Wx' ); my $mock_WxPerlSplashProgress = make_mock_obj_in_class( 'Wx::Perl::SplashProgress' ); $mock_WxPerlSplashProgress->set_true(qw( SetLabelColour SetIcon Show SetValue Update Destroy )); $mock_WxPerlSplashProgress->mock( SetLabel => sub { diag ' SetLabel: '.$_[1] } ); $mock_Win32OLE = make_mock_obj_in_class( 'Win32::OLE' ); $mock_Win32OLE->mock( 'SpecialFolders', sub { shift } ); $mock_Win32OLE->mock( 'AppData', sub { return catdir(qw(data win32), 'Application Data') } ); $mock_Win32OLE->mock( 'StartMenu', sub { catdir(qw(data win32 startmenu)) } ); $mock_Win32OLE->mock( 'Desktop', sub { catdir(qw(data win32 desktop)) } ); $mock_Win32Shortcut = make_mock_obj_in_class( 'Win32::Shortcut' ); $mock_Win32Shortcut->mock( 'Load', sub { my ($self, $filename) = @_; $self->{content} = read_file($filename); return 1; } ); $mock_Win32Shortcut->mock( 'Path', sub { my ($self, $path) = @_; $self->{content} = $path; } ); $mock_Win32Shortcut->mock( 'Arguments', sub { my ($self, $args) = @_; $self->{content} .= ' '.$args . "\r\n"; } ); $mock_Win32Shortcut->mock( 'Save', sub { my ($self, $filename) = @_; write_file($filename, $self->{content} . "writetime ". gmtime() . "\r\n"); return 1; } ); $mock_Win32Shortcut->set_true(qw( ShowCmd Description IconLocation Close )); { no strict 'refs'; *{'Win32::Shortcut::SW_SHOWMINNOACTIVE'} = sub {}; }
8 06/02/12
Test::MockObject 3
$mock_WxPerlSplashProgress->clear(); is( $i->_install_loginscript, 1, '$i->_install_loginscript' ); dump_mock_calls($mock_IFLDesktopLoginScript); $mock_IFLDesktopLoginScript->called_pos_ok( 3, 'install', 'called IFL::Desktop::LoginScript->install' ); dump_mock_calls($mock_WxPerlSplashProgress); $mock_WxPerlSplashProgress->called_pos_ok( 4, 'SetLabel', 'called Wx::Perl::SplashProgress->SetLabel' ); $mock_WxPerlSplashProgress->called_args_pos_is( 4, 2, 'Checking login script' ); $mock_WxPerlSplashProgress->called_pos_ok( 7, 'SetLabel', 'called Wx::Perl::SplashProgress->SetLabel' ); $mock_WxPerlSplashProgress->called_args_pos_is( 7, 2, 'Installing login script...' );
Testing
9 06/02/12
Test::MockModule 1
Helper
sub mock_module { my ($module,$options,@functions) = @_; my $no_auto = defined($options->{no_auto}) ? $options->{no_auto} : 1; my $create_new = defined($options->{create_new}) ? $options->{create_new} : 1; my $testmockmodule = new Test::MockModule($module, no_auto => $no_auto); my $object; if ($create_new) { $object = bless {}, $module; $testmockmodule->mock('new',sub { $logger->log($module,'new',@_); return $object }); } for my $function (@functions) { $testmockmodule->mock($function,sub { $logger->log($module,$function,@_) }); } no strict 'refs'; push @{$module . "::ISA"},'Exporter'; my $module_path = $module; $module_path =~ s{::}{/}xmsg; $module_path .= '.pm'; $INC{$module_path} = "1 (Inserted by mock_module())"; } return $testmockmodule, $object;
10 06/02/12
Test::MockModule 2
Mocking
my ($mock_wx_activex_ie, $mock_wx_activex_ie_object) = mock_module('Wx::ActiveX::IE',{}); my ($mock_wx_activex_event, $mock_wx_activex_event_object) = mock_module('Wx::ActiveX::Event',{},@Wx::Event::EXPORT_OK); my ($mock_wx_panel,$mock_wx_panel_object) = mock_module('Wx::Panel',{}, qw( SetSizer )); my ($mock_wx_boxsizer,$mock_wx_boxsizer_object) = mock_module('Wx::BoxSizer',{}, qw( Add ));
11 06/02/12
Some libraries shared between Unix and Windows; not being tested properly client-side Perl Portability
"perldoc perlport http://perldoc.perl.org/5.8.8/perlport.html "When the code will run on only two or three operating systems, you may need to consider only the differences of those particular systems. The important thing is to decide where the code will run and to be deliberate in your decision. Only worrying about Windows and Unix; OpenVMS support is hard
binmode and chomp - binmode saves headaches on Windows like EOF ^Z; watch out for CR-LF use File::Spec::Functions rather than Unix paths
YES : my $path = rel2abs( catdir(qw( data local cache file.txt )); NO : my $path = './data/local/cache/file.txt';
12 06/02/12
Change tests from path strings to regexes using a quote path separator
my $script = $i->startup('remote'); NO : is( $script, 'scripts/FLIP_real.PL', '$i->startup("remote") script YES : $ps = ($^O eq 'MSWin32') ? "\\" : '/'; $qps = quotemeta $ps; like( $script, qr{ scripts [$qps] FLIP_real.pl \z }xms, '$i>startup("remote") script' ); Note PBP style regex
13 06/02/12
Windows
14 06/02/12
Summary
"perldoc perlport Write cross-platform tests from the outset; convert old ones Mock platform-specific GUI or system library calls Automate tests (life is short) and get as much coverage as possible
Links
WxPerl http://wxperl.sourceforge.net/ WxWidgets http://docs.wxwidgets.org/trunk/ "Perl Testing: A Developer's Notebook" Ian Langworth & chromatic, O'Reilly Media, Inc., 2005 http://preview.tinyurl.com/5k6wnc