As the data structures your code uses become more complex, so will your tests. It's important to verify what actually makes up a data structure instead of simply comparing it to an existing structure. You could iterate through each level of a complex nested hash of arrays, checking each and every element. Fortunately, the Test::Deep module neatens up code testing complicated data structures and provides sensible error messages. How do I do that? Save the following as cmp_deeply.t : use Test::More tests => 1; use Test::Deep; my $points = [ { x => 50, y => 75 }, { x => 19, y => -29 }, ]; my $is_integer = re('^-?\d+$'); cmp_deeply( $points, array_each( { x => $is_integer, y => $is_integer, } ), 'both sets of points should be integers' ); Now run cmp_deeply.t from the command line with prove . It will show one successful test: $ prove cmp_deeply.t cmp_deep....ok All tests successful. Files=1, Tests=1, 0 wallclock secs ( 0.06 cusr + 0.00 csys = 0.06 CPU) What just happened ? cmp_deeply( ) , like most other testing functions, accepts two or three arguments: the data structure to test, what you expect the structure to look like, and an optional test description. The expected data, however, is a special test structure with a format containing special Test::Deep functions. The test file begins by creating a regular expression using re( ) , a function exported by Test::Deep . re( ) declares that the data must match the given regular expression. If you use a regular expression reference instead, Test::Deep believes you expect the data to be a regular expression instead of matching the data against it. Note: re( ) also lets you perform checks on the data it matches. See the Test::Deep documentation for details . Test::Deep 's array_each( ) function creates the main test structure for the test. To pass the test, $points must be an array reference. Every element of the array must validate against the test structure passed to array_each( ) . Passing a hash reference as the test structure declares that every element must be a hash reference and the values of the given hash must match the values in the test structure's hash. In cmp_deeply.t , the hash contains only two keys, x and y , so the given hash must contain only those keys. Additionally, both values must match the regular expression created with re( ) . Test::Deep 's diagnostics are really useful with large data structures. Change $points so that the y value of the first hash is the letter " Q ", which is invalid according to the provided test structure. Save it as cmp_deeply2.t : use Test::More tests => 1; use Test::Deep; my $points = [ { x => 50, y => 75 }, { x => 19, y => 'Q' }, ]; my $is_integer = re('^-?\d+$'); cmp_deeply( $points, array_each( { x => $is_integer, y => $is_integer, } ) ); Now run cmp_deeply2.t with prove -v . The cmp_deeply( ) function will fail with the following diagnostic: $ prove -v cmp_deeply2.t cmp_deep2....# Failed test (cmp_deep2.t at line 11) # Using Regexp on $points->[1]{"y"} # got : 'Q' # expect : (?-xism:^-?\d+$) # Looks like you failed 1 tests of 1. dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay. Failed Test Stat Wstat Total Fail Failed List of Failed ---------------------------------------------------------------------------- cmp_deep2.t 1 256 1 1 100.00% 1 The failure diagnostic shows the exact part of the data structure that failed and explains that the value Q doesn't match the regular expression $is_integer . What about... Q: | What if some values in the data structure may change? | A: | To ignore a specific value, use the ignore( ) function in place of the regular expression. The following example still ensures that each hash in the array has both x and y keys, but doesn't check the value of y : array_each( { x => $is_integer, y => ignore( ), } ); | Q: | What if some keys in the data structure may change? | A: | Suppose that you want to make sure that each hash contains at least the keys x and y . The superhashof( ) function ensures that the keys and values of the structure's hash appear in the given hash, but allows the given hash to contain other keys and values: array_each( superhashof( { x => $is_integer, y => ignore( ), } ) ); Note: Think of sets, supersets, and subsets . Similarly, Test::Deep 's subhashof( ) function ensures that a given hash may contain some or all of the keys given in the test structure's hash, but no others. | Q: | How do I check the contents of an array when I can't predict the order of the elements? | A: | Test::Deep provides a bag( ) function that does exactly this. Save the following as bag.t : use Test::More tests => 1; use Test::Deep; my @a = ( 4, 89, 2, 7, 1 ); cmp_deeply( \@a, bag( 1, 2, 4, 7, 89 ) ); Run bag.t to see that it passes the test. The bag( ) function is so common in test files that Test::Deep provides a cmp_bag( ) function. You can also write bag.t as follows : use Test::More tests => 1; use Test::Deep; my @a = ( 4, 89, 2, 7, 1 ); cmp_bag( \@a, [ 1, 2, 4, 7, 89 ] ); | Where to learn more This section is only a brief overview of the Test::Deep module, which provides further comparison functions for testing objects, methods , sets (unordered arrays with unique elements), booleans, and code references. For more information, see the Test::Deep documentation. |