Porting Test::Builder to Perl 6

Porting Test::Builder to Perl 6

by chromatic
July 28, 2005

Perl 6 development now proceeds in two directions. The first is from the bottom up, with the creation and evolution of Parrot and underlying code, including the Parrot Grammar Engine. The goal there is to build the structure Perl 6 will need. The second direction is from the top down, with the Pugs project implementing Perl 6 initially separate from Parrot, though recent additions allow an embedded Parrot to run the parsed code and to emit valid Parrot PIR code.

Both projects are important and both help the design of Perl 6 and its implementation. Parrot is valuable in that it demonstrates a solid foundation for Perl 6 (and other similar languages); a far better foundation than the internals of Perl 5 have become. Pugs is important because it allows people to use Perl 6 productively now, with more features every day.

Motivation and Design

Perl culture values testing very highly. Several years ago, at the suggestion of Michael Schwern, I extracted the code that would become Test::Builder from Test::More and unified Test::Simple and Test::More to share that back end. Now dozens of other testing modules, built upon Test::Builder, work together seamlessly.

Pugs culture also values testing. However, there was no corresponding Test::Builder for Perl 6 yet--there was only a single Test.pm module that did most of what the early version of Test::More did in Perl 5.

Schwern and I have discussed updates and refactorings of Test::Builder for the past couple of years. We made some mistakes in the initial design. As Perl 6 offers the chance to clean up Perl 5, so does a port of Test::Builder to Perl 6 offer the chance to clean up some of the design decisions we would make differently now.

Internally, Test::Builder provides a few testing and reporting functions and keeps track of some test information. Most importantly, it contains a plan consisting of the number of tests expected to run. It also holds a list of details of every test it has seen. The testing and reporting functions add information to this list of test details. Finally, the module contains functions to report the test details in the standard TAP format, so that tools such as Test::Harness can interpret the results correctly.

Test::Builder needs to do all of these things, but there are several ways to design the module's internals. Some ways are better than others.

The original Perl 5 version mashed all of this behavior together into one object-oriented module. To allow the use of multiple testing modules without confusing the count or the test details, Test::Builder::new() always returns a singleton. All test modules call the constructor to receive the singleton object and call the test reporting methods to add details of the tests they handle.

This works, but it's a little inelegant. In particular, modules that test test modules have to go to a lot of trouble to work around the design. A more flexible design would make things like Test::Builder::Tester much easier to write.

The biggest change that Schwern and I have discussed is to separate the varying responsibilities into separate modules. The new Test::Builder object in Perl 6 itself contains a Test::Builder::TestPlan object that represents the plan (the number of tests to run), a Test::Builder::Output object that contains the filehandles to which to write TAP and diagnostic output, and an array of tests' results (all Test::Builder::Test instances).

The default constructor, new(), still returns a singleton by default. However, modules that use Test::Builder can create their own objects, which perform the Test::Builder::TestPlan or Test::Builder::Output roles and pass them to the constructor to override the default objects created internally for the singleton. If a test module really needs a separate Test::Builder object, the alternate create() method creates a new object that no other module will share.

This strategy allows the Perl 6 version of Test::Builder::Tester to create its own Test::Builder object that reports tests as normal and then creates the shared singleton with output going to filehandles it can read instead of STDOUT and STDERR. The design appears to be sound; it took less than two hours to go from the idea of T::B::T to a fully working implementation--counting a break to eat ice cream.

First Attempts

Translating Perl 5 OO code into Perl 6 OO code was mostly straightforward, despite my never having written any runnable Perl 6 OO code. (Also, Pugs was not far enough along that objects worked.)

What Went Right

One nice revelation is that opaque objects are actually easier to work with than blessed references. Even better, Perl 6's improved function signatures reduce the necessity to write lots of boring boilerplate code.

Breaking Test::Builder into separate pieces gave the opportunity for several other refactorings. One of my favorite is "Replace Condititional with Polymorphism". There are four different types of tests that have different reporting styles: pass, fail, SKIP, and TODO. It made sense to create separate classes for each of those, giving each the responsibility and knowledge to produce the correct TAP output. Thus I wrote Test::Builder::Test, a façade factory class with a very smart constructor that creates and returns the correct test object based on the given arguments. When Test::Builder receives one of these test objects, it asks it to return the TAP string, passes that message to its contained Test::Builder::TestOutput object, and stores the test object in the list of run tests.

O'Reilly Open Source Convention 2005.

[1] [2] [3] Next

Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > Perl > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction