NAME

Test::DNS - Test DNS queries and zone configuration

VERSION

version 0.201

SYNOPSIS

This module helps you write tests for DNS queries. You could test your domain configuration in the world or on a specific DNS server, for example.

use Test::DNS;
use Test::More tests => 4;

my $dns = Test::DNS->new();

$dns->is_ptr( '1.2.3.4' => 'single.ptr.record.com' );
$dns->is_ptr( '1.2.3.4' => [ 'one.ptr.record.com', 'two.ptr.record.com' ] );
$dns->is_ns( 'google.com' => [ map "ns$_.google.com", 1 .. 4 ] );
$dns->is_a( 'ns1.google.com' => '216.239.32.10' );

...

DESCRIPTION

Test::DNS allows you to run tests which translate as DNS queries. It's simple to use and abstracts all the difficult query checks from you. It has a built-in tests naming scheme so you don't have to name your tests (as shown in all the examples) even though it supports the option.

use Test::DNS;
use Test::More tests => 1;

my $dns = Test::DNS->new( nameservers => [ 'my.dns.server' ] );
$dns->is_ptr( '1.1.1.1' => 'my_new.mail.server' );

That was a complete test script that will fetch the PTR (if there is one), warns if it's missing one (an option you can remove via the warnings attribute) and checks against the domain you gave. You could also give for each test an arrayref of expected values. That's useful if you want to check multiple values. For example:

use Test::DNS;
use Test::More tests => 1;

my $dns = Test::DNS->new();
$dns->is_ns( 'my.domain' => [ 'ns1.my.domain', 'ns2.my.domain' ] );
# or
$dns->is_ns( 'my.domain' => [ map "ns$_.my.domain", 1 .. 5 ] );

You can set the follow_cname option if your PTR returns a CNAME instead of an A record and you want to test the A record instead of the CNAME. This happened to me at least twice and fumbled my tests. I was expecting an A record, but got a CNAME to an A record. This is obviously legal DNS practices, so using the follow_cname attribute listed below, the test went with flying colors. This is a recursive CNAME to A record function so you could handle multiple CNAME chaining if one has such an odd case.

New in version 0.04 is the option to give a hashref as the testing values (not including a test name as well), which makes things much easier to test if you want to run multiple tests and don't want to write multiple lines. This helps connect Test::DNS with freshly-parsed data (YAML/JSON/XML/etc.).

use Test::DNS;
use YAML 'LoadFile';
use Test::More tests => 2;

my $dns = Test::DNS->new();
# running two DNS tests in one command!
$dns->is_ns( {
    'first.domain'  => [ map { "ns$_.first.domain"  } 1 .. 4 ],
    'second.domain' => [ map { "ns$_.second.domain" } 5, 6   ],
} );

my $tests = LoadFile('tests.yaml');
$dns->is_a( $tests, delete $tests->{'name'} ); # $tests is a hashref

EXPORT

This module is completely Object Oriented, nothing is exported.

ATTRIBUTES

nameservers($arrayref)

Same as in Net::DNS. Sets the nameservers, accepts an arrayref.

my $dns = Test::DNS->new(
    'nameservers' => [ 'IP1', 'DOMAIN' ],
);

warnings($boolean)

Do you want to output warnings from the module (in valid TAP), such as when a record doesn't a query result or incorrect types?

This helps avoid common misconfigurations. You should probably keep it, but if it bugs you, you can stop it using:

my $dns = Test::DNS->new(
    'warnings' => 0,
);

Default: 1 (on).

follow_cname($boolean)

When fetching an A record of a domain, it may resolve to a CNAME instead of an A record. That would result in a false-negative of sorts, in which you say "well, yes, I meant the A record the CNAME record points to" but Test::DNS doesn't know that.

If you want want Test::DNS to follow every CNAME recursively till it reaches the actual A record and compare that A record, use this option.

my $dns = Test::DNS->new(
    'follow_cname' => 1,
);

Default: 0 (off).

SUBROUTINES/METHODS

is_a( $domain, $ips, [$test_name] )

Check the A record resolving of domain or subdomain.

$ip can be an arrayref.

$test_name is not mandatory.

$dns->is_a( 'domain' => 'IP' );

$dns->is_a( 'domain', [ 'IP1', 'IP2' ] );

Returns false if the assertion fails.

is_ns( $domain, $ips, [$test_name] )

Check the NS record resolving of a domain or subdomain.

$ip can be an arrayref.

$test_name is not mandatory.

$dns->is_ns( 'domain' => 'IP' );

$dns->is_ns( 'domain', [ 'IP1', 'IP2' ] );

Returns false if the assertion fails.

is_ptr( $ip, $domains, [$test_name] )

Check the PTR records of an IP.

$domains can be an arrayref.

$test_name is not mandatory.

$dns->is_ptr( 'IP' => 'ptr.records.domain' );

$dns->is_ptr( 'IP', [ 'first.ptr.domain', 'second.ptr.domain' ] );

Returns false if the assertion fails.

is_mx( $domain, $domains, [$test_name] )

Check the MX records of a domain.

$domains can be an arrayref.

$test_name is not mandatory.

$dns->is_mx( 'domain' => 'mailer.domain' );

$dns->is_ptr( 'domain', [ 'mailer1.domain', 'mailer2.domain' ] );

Returns false if the assertion fails.

is_cname( $domain, $domains, [$test_name] )

Check the CNAME records of a domain.

$domains can be an arrayref.

$test_name is not mandatory.

$dns->is_cname( 'domain' => 'sub.domain' );

$dns->is_cname( 'domain', [ 'sub1.domain', 'sub2.domain' ] );

Returns false if the assertion fails.

is_txt( $domain, $txt, [$test_name] )

Check the TXT records of a domain.

$txt can be an arrayref.

$test_name is not mandatory.

$dns->is_txt( 'domain' => 'v=spf1 -all' );

$dns->is_txt( 'domain', [ 'sub1.domain', 'sub2.domain' ] );

Returns false if the assertion fails.

is_record( $type, $input, $expected, [$test_name] )

The general function all the other is_* functions run.

$type is the record type (CNAME, A, NS, PTR, MX, etc.).

$input is the domain or IP you're testing.

$expected can be an arrayref.

$test_name is not mandatory.

$dns->is_record( 'CNAME', 'domain', 'sub.domain', 'test_name' );

Returns false if the assertion fails.

BUILD

Moose builder method. Do not call it or override it. :)

HASH FORMAT

The hash format option (since version 0.04) allows you to run the tests using a single hashref with an optional parameter for the test_name. The count is no longer 1 (as it is with single tests), but each key/value pair represents a test case.

# these are 2 tests
$dns->is_ns( {
    'first.domain'  => [ map { "ns$_.first.domain"  } 1 .. 4 ],
    'second.domain' => [ map { "ns$_.second.domain" } 5, 6   ],
} );

# number of tests: keys %{$tests}, test name: $tests->{'name'}
$dns->is_a( $tests, delete $tests->{'name'} ); # $tests is a hashref

DEPENDENCIES

Moose

Net::DNS

Test::Deep

AUTHOR

Sawyer X, <xsawyerx at cpan.org>

BUGS

Please report any bugs or feature requests to bug-test-dns at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-DNS. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Test::DNS

You can also look for information at:

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

Copyright 2019 Sawyer X.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

AUTHOR

Sawyer X <xsawyerx@cpan.org>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2019 by Sawyer X.

This is free software, licensed under:

The MIT (X11) License