CPANifying our test framework - or parts of it

[email protected] (Sam Kington) Sat, 10 Sep 2016 03:34:02 +0200
Newsgroups perl.qa
Message-ID <[email protected]>
Hi,

At $WORK we have an extensive test suite that we=E2=80=99ve built up =
over the years, with loads of convenience methods for calling e.g. =
Dancer endpoints and checking returned data structures against what we =
expect. One of our dev team recently left for pastures new, and would =
like to carry on using these tools. How should I best extract this =
functionality into a proper CPAN distribution (ideally using Test2)?

At its most elaborate, our current test code lets you say e.g.

# Assume $self->state('location_id') has been set previously
$self->test(
    title =3D> 'Create a properly megalithic structure',
    call  =3D> [
        POST =3D> '/location/:location_id/build',
        {
            author     =3D> 'Wally Wallington',
            structures =3D> [
                {
                    type     =3D> 'dolmen',
                    material =3D> 'concrete',
                }
            ]
        }
    ],
    expect =3D> {
        http_code =3D> HTTP_CREATED,
        sql_count =3D> 12,
        data      =3D> {
            location_id =3D> $self->state('location_id'),
            build_id    =3D> qr{^ (?<build_id> BUILD \d+ ) $}x,
            built       =3D> $self->true,
            author     =3D> $self->meh,    # It's OK if they call him =
Idiot.
            structures =3D> [
                {
                    _hashref_contains =3D> {
                        structure_id =3D> qr{^ (?<structure_id> STRUCT =
\d+ ) $}x,
                        type         =3D> 'dolmen',
                        material     =3D> 'concrete',

                        # There's probably stuff about where the dolmen
                        # was erected but we ignore that for the purpose
                        # of this test.
                    }
                }
            ]
        }
    }
);

# $self->state('build_id') got set by the named capture in the regex =
above.
$self->test(
    title =3D> 'Turn it to gold because lol',
    call  =3D> [
        PATCH =3D> =
'/location/:location_id/build/:build_id/structure/:structure_id',
        { material =3D> 'gold' }
    ],
    expect =3D> {
        # The same as saying data =3D> { _hashref_contains =3D> { =
material =3D> 'gold' } }
        data_contains =3D> {
            material =3D> 'gold'
        }
    }
);

And if anything doesn=E2=80=99t quite match, it tries to show you what =
did and didn=E2=80=99t match.

The stuff in call gets passed to e.g. Dancer::Test::dancer_response or =
treated as a method call, depending on the invocation. As we share a =
common process with the code being called and tested, we can also =
monitor the database calls being made via DBIx::Class, and there=E2=80=99s=
 some fiendish code for tracking, diagnosing and correcting test errors =
regarding the number of distinct database queries being made.

These are all nice-to-haves rather than something that=E2=80=99s =
inherent to the design of this testing code. What=E2=80=99s really =
useful, and what my ex-colleague is hankering after, is a couple of =
things:

(A) To say, flexibly, =E2=80=9CI want a data structure that looks like =
this=E2=80=9D, and to be able to say (1) this field must look exactly =
like this, (2) this field should look like this, and remember it, (3) =
this field should exist, but I don=E2=80=99t care what its value is, and =
(4) there might be additional fields but I don=E2=80=99t care about them =
for the purpose of this test.

(B) To maintain a collection of placeholders that can be updated by =
tests and used by subsequent tests.

I haven=E2=80=99t looked recently at the Test:: infrastructure, so for =
all I know we may have replicated functionality that we=E2=80=99d missed =
a few years ago when we started coding.

But if this functionality is new and useful, how should I implement it?

Sam
--=20
Website: http://www.illuminated.co.uk/