This works fine, and I'm happy with it, but in my test scripts I have chosen to test that I can't feed improper things to the set routines. As you can see the error reports from carp in _ref_check() turn up in the test output.

t\01-sanity.....................ok
t\02-simple.....................ok 16/0
>> 4 isn't an array reference at all, but a SCALAR
# # Won't accept scalar values
>> HASH(0x1dec550) isn't a reference to an array, but rather a referen+ce to a HASH
# # Won't accept hash values
t\02-simple.....................ok
t\03-transform..................ok 3/0
>> ARRAY(0x1c0ba88) isn't a reference to a subroutine , but rather a r+eference to an ARRAY
t\03-transform..................ok 5/0
>> 4 isn't an array reference at all, but a SCALAR
t\03-transform..................ok 7/0
>> HASH(0x1ab5430) isn't a reference to an array, but rather a referen+ce to a HAS
t\03-transform..................ok
[snip]
All tests successful.
Files=9, Tests=101, 1 wallclock secs ( 0.00 cusr + 0.00 csys = 0.00+ CPU)

If you use Test::More, you can use the diag() function to print diagnostic or debugging information. Test::Harness will suppress it when running tests normally. You can run the test by hand or with the verbose flag and see them.

If you are using Test::More, you can also use Test::Exception and Test::Warning to test for death and warnings. I've had good results with them.

I have very similar logic in a number of my test scripts. I use an eval block and check that the message in $@ matches
what I was expecting. That won't work directly for you since your code uses 'carp' where mine uses 'croak'.

If one of your methods is passed an invalid argument, is there any point carrying on? Rather than carp and return 0, why not just croak?

Coming back to your original question though, you could capture warning messages like this: