Some of the more dynamic languages—I have used this with Perl in particular—allow you to declaratively change the scope of a block of code, much like reflection-base coercion. Although this features carries great potential for evil if you use it to pry open other people’s encapsulation, we use it only for good. It leverages the general principle of using the rules of packages to give tests access to the code they are testing, doing so in a more targeted and purposeful way.
Let’s look at an example using the Class::Std module for Perl object-oriented development (Listing 9-13). Class::Std lets you ascribe methods with traits that represent their visibility, much like the protected
and private
keywords in Java and C++. Using Class::Std, you use the RESTRICTED
and PRIVATE
traits, respectively. Class::Std uses Perl’s dynamic reflection capabilities to intercept calls to these methods and to ensure the calls come from the appropriate scope, either from the class in which they are declared or, in the case of RESTRICTED
, from a class derived from that class.
package My::Class;
use Class::Std;
{
sub some_internal_method : PRIVATE
{
# Do something the world need not see
}
}
1;
If some_internal_method
is sufficiently complex, it is best tested by itself rather than only through its callers. Its declaration as PRIVATE
makes this difficult . . . unless we use our new trick (Listing 9-14).
package My::Test::Class;
use base qw(Test::Unit::TestCase);
sub test_some_internal_method {
my $self = shift;
my $sut = My::Class->new();
my $result;
{
package My::Class;
$result = $sut->some_internal_method();
}
# Assert something about $result
}
The package
declaration within the test fools Class::Std into thinking that some_internal_method
is being called from its own class. The surrounding braces let us restrict the declarative relocation only to the narrow scope of our call like good code citizens. Even if we choose to violate encapsulation for testability, we only do it for the minimum duration for which we need it.
18.118.128.105