Packaging Tests with Code

Many languages use packages or modules as a mechanism to help organize your classes. Many of these provide additional access privileges to code in the same package. Java has a special access level for things in the same package. Ruby modules provide namespaces and are the basis for mixins. Python modules are like classes, but its packages are purely organizational. But be careful. The Perl package declaration defines the name for the class not just the namespace it falls within. This difference is crucial as we will see in a moment.

The privileges of shared package membership give you access to more members of your software under test. Let’s consider how this would impact the testability of the Java code in Listing 9-1.

Listing 9-1: Packages and access level examples in Java

package my.package.example;
class MyClass {
  public void runMe() { }
  protected void inheritMe() { }
  /* package */ void packageMe() { }
  private void hideMe() { }
}

Any class can access the runMe() method. On the other end of the spectrum, only the code in MyClass can invoke the hideMe() method. The inheritMe() method is most commonly understood to be accessible from derived classes. Code within the my.package.example package can invoke the packageMe() method and, as is often overlooked, the inheritMe() method. This last feature provides a significant level of support for testability.

With decreasing frequency, Java developers sometimes put their tests in a special “test” subpackage as in Listing 9-2.

Listing 9-2: Putting tests in a special “test” sub-package

package my.package.example.test;
class MyClassTest {
  public void testRunMe() { } // OK
  public void testInheritMe() { } // Not accessible!
  public void testPackageMe() { } // Not accessible!
  public void testHideMe() { } // Not accessible!
}

This is often done with the thinking that it is necessary to keep it separate from the production code. The Java packages correspond to directories relative to the root directories defined in the classpath. Since the classpath can have multiple directories, you can separate the tests from the code under test by putting them in the same package under different classpath entries with greater accessibility (see Listing 9-3).

Listing 9-3: Putting tests in the same package as the software under test

package my.package.example;
class MyClassTest {
  public void testRunMe() { } // OK
  public void testInheritMe() { } // OK
  public void testPackageMe() { } // OK
  public void testHideMe() { } // Not accessible!
}

We have now tripled our potential testing coverage simply by changing the package for our test code. Defining separate classpath roots for the test and production code allows us to cleanly keep them separate.

This approach does not work with Perl. Perl has a PERL5LIB environment variable that helps define a search path for Perl packages, somewhat similar to Java’s classpath. However, the package declaration works a little differently, as shown in Listing 9-4.

Listing 9-4: Package declarations in Perl work differently than in Java.

package My::Package::Example::MyClass;
use Class::Std;
{
sub testMe { }
}
1;

This package statement declares not only the path from the root to the module, but the name of the module itself. Declaring your test in the same package would make it a part of the module, potentially changing the behavior of the code under test for all instances: a very undesirable trait in a test. Declaring the package with an extra component becomes essential to keep the test and production code logically separate, even if they are already physically separate in different directories.

Where the language permits, packages provide a useful way to give your tests elevated access to the code they are testing. The semantics of the language determine whether you can leverage that behavior in your particular programming environment.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.103.154