All posts

Unit Testing Perl Applications with Test::More

3 min read Perl Unit Testing TDD Jenkins CI

Learn how to write unit tests for Perl applications using Test::More, generate TAP reports, measure code coverage, and integrate with Jenkins CI.


Introduction

Unit testing is an important part of maintaining long-lived Perl applications.

In this article, we will walk through:

  • Installing and using Test::More
  • Organizing test files
  • Writing basic test cases
  • Understanding common assertions
  • Generating TAP reports
  • Measuring code coverage
  • Integrating tests with Jenkins CI

1. Install Test::More

Fedora

sudo dnf install perl-Test-Simple

RHEL

sudo yum install perl-Test-Simple

Ubuntu

sudo apt-get install libtest-simple-perl

2. Test File Conventions

Naming

Test files should:

  • Start with Test
  • End with .t
  • Be stored in the t/ directory

Recommended naming convention:

Test<ModuleName>.t

Example:

TestVendorProduct.t

Indentation

Use:

  • 4 spaces
  • No tabs

Make sure your editor is configured correctly before writing tests.


3. Create Your First Test File

#!/usr/bin/env perl

use strict;
use warnings;
use Test::More tests => 2;

my $got = 'Hello World';
my $expected = 'Hello World';

is( $got, $expected, 'Say Hello to World' );

my $got2 = 'Hello Moon';
my $expected2 = 'Hello Mars';

is( $got2, $expected2, 'Say Hello to Mars' );

Output:

1..2
ok 1 - Say Hello to World
not ok 2 - Say Hello to Mars
#   Failed test 'Say Hello to Mars'
#   at /home/kyan/perl-unittest/t/TestUnitCase.t line 12.
#          got: 'Hello Moon'
#     expected: 'Hello Mars'
# Looks like you failed 1 test of 2.

4. Understanding Test Plans

When writing a test file, you usually know how many tests will be executed.

There are two common ways to define the number of tests.

Static Test Plan

use Test::More tests => 2;

Using done_testing

use Test::More;

# Testing code...

done_testing(2);

If you do not know how many tests will run in advance, you can simply use:

done_testing();

5. Common Assertions

Basic Assertions

ok

ok($got eq $expected, $test_name);

is

is($got, $expected, $test_name);

isnt

isnt($got, $expected, $test_name);

Regular Expression Assertions

like

like($got, qr/expected/, $test_name);

unlike

unlike($got, qr/expected/, $test_name);

Comparison Assertions

cmp_ok

cmp_ok($got, $op, $expected, $test_name);

# Equivalent to:
# ok($got eq $expected);

cmp_ok($got, 'eq', $expected, 'this eq that');

Object and Module Assertions

can_ok

can_ok($module, @methods);
can_ok($object, @methods);

isa_ok

isa_ok($object,   $class, $object_name);
isa_ok($subclass, $class, $object_name);
isa_ok($ref,      $type,  $ref_name);

new_ok

my $obj = new_ok($class);

my $obj = new_ok($class => \@args);

my $obj = new_ok($class => \@args, $object_name);

require_ok

require_ok($module);
require_ok($file);

use_ok

BEGIN { use_ok($module); }

BEGIN { use_ok($module, @imports); }

Deep Comparison Assertions

is_deeply

is_deeply($got, $expected, $test_name);

eq_array

my $is_eq = eq_array(\@got, \@expected);

eq_hash

my $is_eq = eq_hash(\%got, \%expected);

eq_set

my $is_eq = eq_set(\@got, \@expected);

Miscellaneous Assertions

pass

pass($test_name);

fail

fail($test_name);

subtest

use Test::More tests => 1;

subtest 'An example subtest' => sub {

    plan tests => 2;

    pass('This is a subtest');
    pass('So is this');
};

6. Diagnostics

diag

diag(@diagnostic_message);

note

note(@diagnostic_message);

explain

my @dump = explain @diagnostic_message;

7. Special Testing Blocks

SKIP Block

SKIP: {

    skip $why, $how_many if $condition;

    # Normal testing code goes here
}

TODO Block

TODO: {

    local $TODO = $why if $condition;

    # Normal testing code goes here
}

8. Continuous Integration with Jenkins

Application Environment Configuration

Before running tests in Jenkins, your application environment must be configured correctly.

In many cases, configuration files differ between environments such as:

  • Development
  • Staging
  • Production

A useful Jenkins plugin for managing configuration files is:

After installing the plugin:

  1. Go to Manage Jenkins → Managed files
  2. Add a new configuration file
  3. Configure your Jenkins job
  4. Enable Provide Configuration files
  5. Define the target location where the file should be placed

9. Generate TAP Test Reports

Install TAP::Harness::Archive

Fedora

sudo dnf install perl-TAP-Harness-Archive

RHEL

sudo yum install perl-TAP-Harness-Archive

Ubuntu

sudo apt-get install libtap-harness-archive-perl

Generate Test Reports

Create an output directory and run prove:

mkdir -p output

prove -r t/ --archive output

You can find the generated test results in the output/ directory.


Publish TAP Reports in Jenkins

Install the Jenkins TAP plugin:

Configure your Jenkins job:

  1. Add a post-build action
  2. Select Publish TAP Results
  3. Use the following test file pattern:
output/**/*.t

10. Generate Code Coverage Reports

Install Devel::Cover

Fedora

sudo dnf install perl-Devel-Cover

RHEL

sudo yum install perl-Devel-Cover

Ubuntu

sudo apt-get install libdevel-cover-perl

Install Devel::Cover::Report::Clover

This package is not commonly available through system package managers.

Install it from CPAN:

cpan install Devel::Cover::Report::Clover

Modern alternative:

cpanm Devel::Cover::Report::Clover

Generate Coverage Reports

cover --report clover

Publish Coverage Reports in Jenkins

Install the following Jenkins plugins:

Configure your Jenkins job:

Publish Clover Coverage Report

  • Clover report directory: cover_db
  • Clover report file name: clover.xml

Publish HTML Reports

  • HTML directory to archive: cover_db
  • Index page: coverage.html
  • Report title: Coverage Reports

Example Jenkins Build Script

prove -r t/ --archive output

cover --report clover

Known Issues

Clover Plugin 404 Issue

Some versions of the Jenkins Clover Plugin may generate a broken Coverage Report link that returns a 404 error.

As a workaround, you can publish the generated HTML report using the HTML Publisher Plugin instead.


References

  1. Test::More Documentation
  2. Devel::Cover Documentation
  3. Jenkins TAP Plugin
  4. Jenkins HTML Publisher Plugin
  5. Config File Provider Plugin

Comments