If you really want to make something a habit, find a way to do it without thinking about it. I like to automate the things I value so I never do them incorrectly, incompletely, or infrequently. Thus Test::Perl::Critic allows you to add customizable Perl::Critic tests to your test suites, so you can ensure that you’ve followed local style.

I’ve been part of the Perl QA group for around five years. In that time, we’ve built dozens of wonderful test modules around a common backend library and a common protocol, evangelized testing and quality to the Perl 5 and Perl 6 developers, spread the expectation and understanding of good testing to CPAN contributors and more, and even built automated systems to check various quality measures of public code.

Tests aren’t the only measure of quality; maintainability for code you expect to maintain is also highly important.

Damian Conway’s Perl Best Practices is a good start for evaluating your coding style and practices. Any maintainability guidelines should start there.

One of the most interesting developments in Perl 5 in recent years is Adam Kennedy’s PPI, a Perl document parser. It can parse most Perl code without actually executing it. This has powerful implications for static code analysis.

Shortly after the advent of PBP and PPI, Jeffrey Ryan Thalhammer created Perl::Critic, an extensible PPI-based static analysis tool. It builds on the recommendations of PBP and adds other metrics to identify source code that varies from local standards.

If you really want to make something a habit, find a way to do it without thinking about it. I like to automate the things I value so I never do them incorrectly, incompletely, or infrequently. Thus Test::Perl::Critic allows you to add customizable Perl::Critic tests to your test suites, so you can ensure that you’ve followed local style.

Installation

I used the CPAN shell to install the module. Everything went well. Although I already had Perl::Critic and PPI installed, both distributions move frequently, so I needed to install new versions. There were a few other dependencies, including Perl::Critic::Utils, Test::Object, and Test::Simple (0.64). The instalation worked flawlessly.

Usage

The default usage is simple. I added the example from the documentation as t/critic.t to my Test::MockObject directory. That’s all it took to get started.

  #! perl

  use strict;
  use warnings;

  use Test::Perl::Critic;
  all_critic_ok();

That found some violations:

  $ perl -Ilib t/critic.t
  1..2
  not ok 1 - Test::Perl::Critic for "blib/lib/Test/MockObject.pm"
  #   Failed test 'Test::Perl::Critic for "blib/lib/Test/MockObject.pm"'
  #   in /usr/lib/perl5/site_perl/5.8.8/Test/Perl/Critic.pm at line 95.
  # 
  # Perl::Critic found these violations in "blib/lib/Test/MockObject.pm":
  # Stricture disabled at line 126, column 3.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 301, column 3.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 314, column 3.  See page 429 of PBP.  (Severity: 5)
  not ok 2 - Test::Perl::Critic for "blib/lib/Test/MockObject/Extends.pm"
  #   Failed test 'Test::Perl::Critic for "blib/lib/Test/MockObject/Extends.pm"'
  #   in /usr/lib/perl5/site_perl/5.8.8/Test/Perl/Critic.pm at line 95.
  # 
  # Perl::Critic found these violations in "blib/lib/Test/MockObject/Extends.pm":
  # Stricture disabled at line 54, column 2.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 71, column 3.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 127, column 5.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 150, column 3.  See page 429 of PBP.  (Severity: 5)
  # Stricture disabled at line 163, column 2.  See page 429 of PBP.  (Severity: 5)
  # Looks like you failed 2 tests of 2.

There are six places in the distribution where I disabled strictures. This is okay; it’s doing complex things. Of course, I wanted to browse the code to see if there were any errors. This exercise showed me one spot where I disabled symbolic reference checking in far too wide a scope. I fixed that potential bug. How nice!

Customizing the Behavior

Unfortunately, all of these symbolic references are necessary for the modules to work properly. If I want to keep these tests, I would need some mechanism to allow their use but still pass the check. Test::Perl::Critic uses the .perlcriticrc file, as documented in Perl::Critic. I added an empty file in t/perlcriticrc and added a line to the test file:

        use Test::Perl::Critic -profile => 't/perlcriticrc';

The configuration file needs something in it, and to prevent these particular policies from running, I need to know the name of the policy being violated. Test::Perl::Critic supports a -verbose flag to change its output message. I chose to pass a format string to show only the name of the failed policy:

        use Test::Perl::Critic -verbose => '%p';

I recommend this only for debugging; it changes the entire debugging message. Another option was verbosity level 9, but I wanted only the name of the policy to disable. I could have come up with my own custom format string (and that’s probably the right answer), but this worked for me.

My resulting t/perlcriticrc file is:

        [-TestingAndDebugging::ProhibitNoStrict]

Unfortunately, my tests continued to fail until I noticed that I had used policy in the test file instead of profile. Oops. Beware.

There are other customizations too. For example, the default seems to report only failed policies at severity 5 (the least severe). I was curious about how the code looked at a lower severity, so I added to the use line:

        -severity => 3;

That gave many more warnings. Unfortunately, I found no easy way to set the default severity in my t/perlcriticrc file, but that would have been useful. (I think this is a shortcoming — or at least a deliberate design decision — of Perl::Critic instead of the test module.)

Conclusions

I recommend Test::Perl::Critic, especially if you’ve already decided which policies you do and do not support. If you haven’t discussed that yet with your team, the default severity of 5 (again, the least severe level) is a decent choice.

This module made the right choices. Adding a separate test and policy file to each distribution may seem like busy work, but it allows you to change the severity and rules per project. Using a global .perlcriticrc file would be a mistake. Not only can you improve the compliance of separate projects individually as necessary, but I have some distributions that use some constructs very deliberately but not others. Distributing a policy file (with the appropriate severity) with each distribution helps prevent nasty failures elsewhere.

One question might arise. If you distribute your code, should you distribute these test files? I agree with the authors; it’s inappropriate. These are developer-side tests and represent little value for users. They represent tremendous potential value for authors, however. It’s worth experimenting with what this reveals about your code.