Thread

Posted on Mon Mar 3 23:20:32 2008 by random
Defining optional hash keys
Is there any possibility to define an optional hash-key for comparison? I though about something like:
$data_expect = { name => 'tester', data => ignore(), records => 0, ignore('husband') => 'joey' };
Here data can be of any type, but the hash key must exists, whereas for the haskey husband it is allowed to be non-existing at all, but if it exists it must still match 'joey' (ignore could be used to ignore that too). Of course that would require that comparisions would check the hashkeys, not the values. Any thoughts on that?
Direct Responses: 7257 | Write a response
Posted on Tue Mar 4 00:10:55 2008 by fergal in response to 7254
Re: Defining optional hash keys

At the moment there is no builtin way to do this but you can make a way to do it and you can even make it convenient (if slightly inefficient).

sub opthash { my ($must_have, $can_have) = @_; # combine the mandatory and optional fields to make a new hash with all of them together my %upper_limit = %$must_have, %$can_have; return all(superhash($must_have), subhash($upper_limit)) } $data_expect = opthash( { name => 'tester', data => ignore(), records => 0 }, { husband => 'joey', } }

The superhash will ensure that all of the mandatory fields exist. The subhash allows the optional fields to be missing but if they are present they must match the spec.

It's a bit inefficient because you end up checking all the mandatory fields twice but that's not such a big deal (particularly as Test::Deep caches comparison results during a run of is_deeply).

If you want you can also use code() which allows you to have data passed to a function for comaparison but that looses diagnostic info.

I think it's really time for me to write a doc on how to extend Test::Deep with home-made comparators. The API has been stable for years now.

Hope that helps. If I had time, I think I would alter Test::Deep::Hash to allow optional keys, then provide a function in Test::Deep to give an interface to this. If you feel like digging in, I'm happy to review and apply patches.

Direct Responses: 7275 | Write a response
Posted on Thu Mar 6 00:22:09 2008 by random in response to 7257
Re: Defining optional hash keys
I deployed your proposed workaround and it works great so far (performance is not a real issue for testing in my case. I'm using this sub (typos corrected):
sub opthash { my ($must_have, $can_have) = @_; return all(superhashof($must_have), subhashof({ %$must_have, %$can_have }) ); }
Would it make sense to add this to Test::Deep directly and add the pod for it stating that it is not optimized and should not be used on large hashes where performance is mandatory? Cheers
Direct Responses: 7291 | Write a response
Posted on Sat Mar 8 16:42:20 2008 by fergal in response to 7275
Re: Defining optional hash keys

Hmmm, while I think it's definitely a useful feature, if I was actually going to add it to Test::Deep, I think I would do it differently. Something like your original suggestion:

$expect { key => "value", opt_key("key2") => "value2", };

And modify the Test::Deep::Hash to recognise this. I don't really have time right now but if you're interested, I would do something like:

sub opt_key { my $key = shift; return Test::Deep::OptionalKey->new($key); }

and modify the code in descend() in Test::Deep::HashKeysOnly to detect Test::Deep::OptionalKey objects and not add them to @missing then modify Test::Deep::HashElements to handle OptionalKey correctly too.

It's not a terribly big job, I just don't have time to do it and write the tests and the docs.

Write a response