#!/usr/local/bin/perl -w use strict; #use Test::More 'no_plan'; use Test::More tests => 28; use Test::Exception; my $CLASS; { package Foo; sub bar { return 'original value'; } sub baz { return 'original baz value'; } } BEGIN { chdir 't' if -d 't'; use lib '../lib'; $CLASS = 'Sub::Override'; use_ok($CLASS) || die; } can_ok($CLASS, 'new'); my $override = $CLASS->new; isa_ok($override, $CLASS, '... and the object it returns'); can_ok($override, 'replace'); throws_ok { $override->replace('No::Such::Sub', '') } qr/^\QCannot replace non-existent sub (No::Such::Sub)\E/, "... and we can't replace a sub which doesn't exist"; throws_ok { $override->replace('Foo::bar', 'not a subref') } qr/\(not a subref\) must be a code reference/, '... and only a code reference may replace a subroutine'; ok($override->replace('Foo::bar', sub { 'new subroutine' }), '... and replacing a subroutine should succeed'); is(Foo::bar(), 'new subroutine', '... and the subroutine should exhibit the new behavior'); ok($override->replace('Foo::bar' => sub { 'new subroutine 2' }), '... and we should be able to replace a sub more than once'); is(Foo::bar(), 'new subroutine 2', '... and still have the sub exhibit the new behavior'); can_ok($override, 'override'); ok($override->override('Foo::bar' => sub { 'new subroutine 3' }), '... and it should also replace a subroutine'); is(Foo::bar(), 'new subroutine 3', '... and act just like replace()'); can_ok($override, 'restore'); throws_ok { $override->restore('Did::Not::Override') } qr/^\QCannot restore a sub that was not replaced (Did::Not::Override)/, '... and it should fail if the subroutine had not been replaced'; $override->restore('Foo::bar'); is(Foo::bar(), 'original value', '... and the subroutine should exhibit the original behavior'); throws_ok { $override->restore('Foo::bar') } qr/^\QCannot restore a sub that was not replaced (Foo::bar)/, '... but we should not be able to restore it twice'; { my $new_override = $CLASS->new; ok($new_override->replace('Foo::bar', sub { 'lexical value' }), 'A new override object should be able to replace a subroutine'); is(Foo::bar(), 'lexical value', '... and the subroutine should exhibit the new behavior'); } is(Foo::bar(), 'original value', '... but should revert to the original behavior when the object falls out of scope'); { my $new_override = $CLASS->new('Foo::bar', sub { 'lexical value' }); ok($new_override, 'We should be able to override a sub from the constructor'); is(Foo::bar(), 'lexical value', '... and the subroutine should exhibit the new behavior'); ok($new_override->restore, '... and we do not need an argument to restore if only one sub is overridden'); is(Foo::bar(), 'original value', '... and the subroutine should exhibit its original behavior'); $new_override->replace('Foo::bar', sub {}); $new_override->replace('Foo::baz', sub {}); throws_ok { $new_override->restore } qr/You must provide the name of a sub to restore: \(Foo::bar, Foo::baz\)/, '... but we must explicitly provide the sub name if more than one was replaced'; } { package Temp; sub foo { 23 } sub bar { 42 } my $override = Sub::Override->new('foo', sub { 42 }); $override->replace('bar', sub { 'barbar' }); main::is(foo(), 42, 'Not fully qualifying a sub name will assume the current package'); $override->restore('foo'); main::is(foo(), 23, '... and we should be able to restore said sub'); $override->restore('Temp::bar'); main::is(bar(), 42, '... even if we use a full qualified sub name'); }