It's good to check that your code handles error conditions correctly, but dies_ok() in Test::Exception is too blunt an instrument to do it.

Consider this code that checks that the func() subroutine dies if not passed an argument.

#!/var/perl/bin/perl

use warnings;
use strict;

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

sub func {
    die 'Must pass arg' unless defined $_[0];
}

# Test for failures if the arg is not passed.
dies_ok(   sub { func() }, '#1: Dies without an argument' );
throws_ok( sub { func() }, qr/Must pass arg/, '#2: Throws without an argument' );
lives_ok(  sub { func(42) }, '#3: Lives with an argument' );

# Oops, we made a typo in our function name, but this dies_ok() still passes.
dies_ok(   sub { func_where_the_name_is_incorrect() }, '#4: Func dies without an argument' );

In case #4, the call to func_where_the_name_is_incorrect() indeed dies, but for the wrong reason. It dies because the function doesn't exist. If we had used throws_ok instead of dies_ok like so:

throws_ok( sub { func_where_the_name_is_incorrect() }, qr/Must pass arg/, '#4: Func dies without an argument' );

then the test would have failed because the exception was incorrect:

#   Failed test '#4: Func dies without an argument'
#   at foo.t line 19.
# expecting: Regexp ((?^:Must pass arg))
# found: Undefined subroutine &main::func_where_the_name_is_incorrect called at foo.t line 19.

Why do I post this? I found an example of this in some code I was working with, where the test had been passing for the wrong reason for the past six years. Take the time to be specific in what you check for.