Organizing Perl Test Files showed the basic framework I use for individual test files written to work with Perl's testing tools. While I gladly take advantage of frameworks such as Test::Class and Test::Routine, sometimes I need something a little simpler.

The discipline of the organization method I explained in the previous article offers the benefit of simplicity and some discoverability. It also allows my team to run only a portion of the test suite as needed.

One of our products has a web interface. We have quite a few tests for this, but because they run through the whole web stack, they're quite a bit slower than tests for the business model API directly. (We want to make sure the web site always works, so we have some exhaustive tests.)

We've divided many of these tests up into discrete files based on controllers and subsets of controllers. The administrative section has a few test files. The data sharing section has a few test files. The public section has a few test files.

Some of these tests take a while to run—multiple seconds. If you're working on a bug or a feature in one specific action, waiting more than a couple of seconds for test results is way too long.

That's where the named functions I use to group related tests come in. Assume you have a test file testing the admin features, and one of those named functions is test_admin_console_list_expired_users(). You could edit the main() function to comment out all of the other tests. Alternately, add a simple code block to main():

sub main {
    my @args  = @_;

    if (@args) {
        for my $name (@args) {
            die "No test method test_$name\n"
                unless my $func = __PACKAGE__->can( 'test_' . $name );
            $func->();
        }

        done_testing;
        return 0;
    }

    # ... run all tests here

    done_testing;
    return 0;
}

... which will interpret any arguments passed to this test file as names of test functions to run. To run only the test function test_admin_console_list_expired_users(), use the prove command line:

$ prove -l t/web/admin_console.t :: admin_console_list_expired_users

The double-colon tells prove to stop looking for its own arguments and to pass the following arguments to the test file. With that invocation, only the requested test will run.

For this strategy to work, your test functions must be independent within your test files. The data the expired users list test needs to run must already be in place, untouched by other tests, or the test will fail. That's good test discipline anyway, though.

This strategy only saves us several seconds every time we use it, but saving several seconds from asking the question "Does this work?" to getting the answer is a huge benefit in the moment.

There may be easier ways to handle this, but so far this has met a real need without forcing us to divide our tests into even finer granularity (more files to manage and remember) but surely allowing us to run tests at the level we need most. For any test file which takes longer than two or three seconds to run in parallel, this has been a huge benefit.

Organizing Perl Test Files

| No Comments

As the Testing section of the Modern Perl book tries to explain, Perl tests are just Perl code. Sure, the libraries built on top of Test::Builder extend your testing toolbox by providing new functions to help you write tests, but tests are just code.

The first implication of that statement is that, if you know how to write code, you can write tests.

The second implication of that statement—one which takes longer to realize—is that test code has maintenance costs just like code code does.

I like using Test::Class and Test::Routine when they make sense, but some tests are easier to write when they start with a little less ceremony. (I also like to refine and reorganize my tests when better organization suggests itself.) In those cases, I start with something simpler:

#!/usr/bin/env perl

use Modern::Perl '2013';

use Test::More;

use lib 't/lib';

exit main( @ARGV );

sub main {
    my @args  = @_;

    test_this();
    test_that();
    test_the_other();

    done_testing;
    return 0;
}

Every set of test assertions I can collect into a named group is a function. (You can even use Test::More subtests if you like.) When a function gets too long—when I can come up with a new name for a subset of the behavior under test—it's time for a new function.

(I write exit main( @ARGV ); as the first non-pragma line of the program so that there are no order-of-initialization concerns from Perl's compile-time/run-time distinctions. If that means nothing to you, carry on; it's a subtle thing that almost never causes problems.)

Grouping everything under functions also has the benefit of reminding other developers that it's okay to write more functions. For example, if every test for every action in a controller ought to verify access permissions, it's easy to write a helper function to set up an environment with and without access permissions to test both ways. (Some of these test functions get promoted to project-specific test libraries.)

I suspect that the discipline of writing test files as if they were code, not just scripts, is that it provides a subtle pressure to continue to write tests as if it were code, with all of the discipline and factoring and maintenance work that implies. It's easier to take something a little messy and make it a lot messier, but for me at least it's nice to start with something clean and leave it at least that clean. (This is probably why dishes pile up in my office when the dishwasher needs to be emptied.)

Please note that these test names do not necessarily correspond to individual methods or functional units within the code under test. That is, just because I might have a method called analyze_country_performance_metrics(), I don't have a single test function to test that method. I might have several. I might have none (if other code tests it adequately). Each test function tests a single, coherent, nameable feature, like "editing a user account with invalid data" or "exporting person search to CSV".

This is subtle, but it's important. Think in terms of features you can discuss with end users. (Perhaps that means I find Test::Class and its friends more useful for unit-style testing.) This helps you navigate your way through the test suite and your issue tracker by giving everyone canonical names to describe tests.

A little structure is valuable. It's easy to set up and to maintain. Yes, there's a little bit of duplication in calling functions and declaring them, but the benefit of navigation and nomenclature outweigh that many times over. The specific structure of your test files doesn't matter as much as that structure exists and you take advantage of it.

(It also lets you run test functions individually as you're fixing bugs or adding features, but the mechanics of that are the subject of another post.)

SEO Tips for Perl Advocacy

| 1 Comment

A few moments after I posted Perl Web Pages and Search Engines, someone at Big Blue Marble said "You should explain what you've done technically."

I run a few sites, including this one. I've worked on the technology behind a few others. We've been working on a short article called Simple SEO Tips (in Ten Minutes) to explain a few easy techniques to help web crawlers find out exactly what a web page is about and to display it to the people who could most benefit from it.

It's still a work in progress at the moment, but I've used it those techniques on several BBM projects so far, including Trendshare and its investment guide for novice investors, as well as ModernPerl.net, and even the nascent family recipe site Blender Recipe Reviews.

I know Propaganda.pm tracks the performance of Perl advocacy campaigns now. If you have a blog or website or articles about the Perl programming language, maybe some of these tips will help you get more and better traffic, help crowd old tutorials and outdated out of the top search results, and improve the visibility of our favorite programming language.

Imagine, for example, if every one of the generated pages of search.cpan.org had the phrase "the Perl programming language" in it and relevant meta description tags....

Perl Web Pages and Search Engines

| 2 Comments

While browsing Perl 7, the story of, I noticed that Perl6.org has a HTML <meta name="description" /> tag. I also noticed that it claimed that "Perl 6 is the next major version of Perl".

(Don't worry; I fixed that.)

I spent a lot of time in the past year working with family members on websites. Part of that work required me to learn a lot more about SEO and usability. (Thank goodness for schema.org, for example.)

The meta description is highly useful. It allows a search engine to display a contextually accurate and complete (short) phrase when the engine lists the page in a result set, rather than a snippet of semi-highlighted keywords taken out of context. This, of course, can make the result list much more useful for readers.

(I've spent a lot of time adding this description to the pages here on this site. I haven't finished. I've noticed it's improved search engine traffic here and elsewhere though; it's worth it.)

When the inevitable discussion of "How can the active Perl community make itself and all of its great work more visible to the rest of the Internet?" comes back in the next two or three months, keep in mind the idea of robustness: having useful information is important, but presenting that information in a way that's easy for machines to interpret is just as important.

(I hope that Propaganda.pm is listening. I'm glad to see that perl.org is too.)

The Fat Comma and Clarity

One of the simplest but most useful examples of TIMTOWTDI in the design of Perl is the fat comma operator (=>), which acts like a regular comma except that it automatically quotes barewords used as its left operands. It also looks like an arrow, so it leads from left to right and implies a stronger association between its operands than the normal comma does.

You can see this in hash initializers:

my %dogs = (
    alpha => 'Rodney',
    clown => 'Lucky',
    puppy => 'Rosie',
);

... which makes the association of key with value clearer than the standard comma does:

my %dogs = (
    'alpha', 'Rodney',
    'clown', 'Lucky',
    'puppy', 'Rosie',
);

... and much clearer than even an idomatic bare list initializer might:

my %dogs = qw( alpha Rodney clown Lucky puppy Rosie );

You can use the fat comma in place of the normal comma almost anywhere its autoquoting semantics don't change your code. (I can't think of any at the moment, but there might be a JAPH somewhere that proves me wrong in a strange and wonderful away.) With Moose and other semi-keyword libraries using lots of named parameters, you often see code like:

has 'name' => (
    is => 'rw',
    isa => 'Str'
);

has 'age' => (
    is => 'rw',
    isa => 'Int'
);

There's nothing wrong with that code, but I confess that it confuses me a little bit every time I skim it. I understand the desire to emphasize the name of the attribute (name and age) and to suggest that all of the other parameters provided to has are merely refinements of the behavior of the attribute, such that grouping them by the parentheses makes them into a single visual unit associated with the attribute's name, but I never quite seem to read it that way.

(The lack of autoquoting in this example bothers me too.)

I started to think about this when I read some production code I'd written:

return $self->collaborative_collection( Name => $identifier );

The arguments to collaborative_collection() are the name of the collection and a unique identifier for the collaborative making the request. There's no logical connection between the name and the identifier. I only used the fat arrow for its autoquoting behavior.

The API call looked as if I were passing a single keyword argument, which isn't the case at all. I was passing two distinct arguments.

The temptation to use less punctuation (removing quotes) by using a little more punctuation (a fatter comma) had let me write code that would mislead other readers. If Perl had a different syntax for keyword arguments, I wouldn't have had this problem, but that's not going to fix this code here and now.

After some reflection, I decided that the association that the visual appearance of the fat comma implies is important enough to the reading of code that we should only use it where that relationship is present in the semantics of the code. Sure, it's a little extra punctuation that we don't have to type, but it's clearer code when we read it, and that's more important after all.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

Read Modern Perl online for free!

affiliated with ModernPerl.net

Recent Comments

  • Richard Foley: Good job. read more
  • Richard Foley: Too many people appear unaware of the need to promote read more
  • TheWhip: I think trivialities are no problem for Propaganda.pm read more
  • Alex Beamish: Looks like you might have cut and pasted your code read more
  • Przemek Wesolek: OK, "perlbrew install 5.16.1 --as 5.16_threads -Dusethreads --force" fixed this. read more
  • Przemek Wesolek: I've just been hit by the fact that upgrade-perl ignores read more
  • Amitai Schlair: If the bits of which you need to manage your read more
  • perlpilot: The first part of your post reminded me of this read more
  • nnutter: Yes, I should run the debugger and figure out an read more
  • Jamess: I find that Enbugger works rather well for that type read more

Categories

Pages

Find recent content on the main index or look in the archives to find all content.


Sponsored by Blender Recipe Reviews

Powered by the Perl programming language

what is programming