Cody Fendant has asked for the wisdom of the Perl Monks concerning the following question:

I'm just testing that I've got exceptions for all the things which can go wrong.

How can I force DBI's prepare() method to fail?

I've tried putting an empty statement in there, bad MySQL code, non-ASCII characters, I can't seem to make it fail. It will fail when it tries to execute of course, but I have code for that anyway.

Can someone please let me know how to guarantee an error on prepare()? Is the trick in how I set up my DBI in the first place, with RaiseError, PrintError, PrintWarn etc?

  • Comment on How can I make DBI's prepare() fail? [MySQL]

Replies are listed 'Best First'.
Re: How can I make DBI's prepare() fail? [MySQL]
by kschwab (Vicar) on Dec 23, 2018 at 16:32 UTC
    You can make it fail if you enable server-side prepare:
    $dbh = DBI->connect( "DBI:mysql:database=test;host=localhost;mysql_server_prepare=1" );
    Then, try to prepare some invalid SQL. I used $sth->prepare("? WHEE") and got this:
    DBD::mysql::db prepare failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? WHEE' at line 1 at test.pl line 9.
    prepare failed at test.pl line 11.
    
Re: How can I make DBI's prepare() fail? [MySQL]
by Corion (Patriarch) on Dec 23, 2018 at 08:10 UTC

    Either you just replace the method with a failing method:

    { local *DBI::db::prepare = sub { die "In prepare" }; dies_ok my_framework_connect(); }

    ... or you use a different database driver. DBD::mysql doesn't do anything in ->prepare, but other database drivers may compile the statement then and there.

      DBD::mysql doesn't do anything in ->prepare

      Wait, what? Why am I doing prepare(), then execute()? Just so I can have parameterised queries?

        You're following ->prepare, ->execute because that's how the DBI API is designed. A DBD is free to do as much or as little work in any of the two steps. I think DBD::mysql merely stores the SQL string in ->prepare and does all the checking etc. in ->execute. You can switch to just using ->selectall_arrayref, but that way you lose easy access to caching. Also, should you ever switch to a different database, it might do more work during ->prepare and thus that phase might raise errors.

Re: How can I make DBI's prepare() fail? [MySQL]
by Anonymous Monk on Dec 23, 2018 at 08:12 UTC
    I'm just testing that I've got exceptions for all the things which can go wrong.
    This seems like a good candidate for Mock objects. Instead of a true DBI object, create an object that would fail the first prepare call it gets and pass it to your code in your tests.

      With respect, that doesn't make sense in the context of what I'm trying to do. If I had a bad DBI object, I'd already know that from my connect() code.

      1. connect
      2. prepare
      3. execute

      Your proposal is that I test number 2 by failing number 1. I'm testing all of them.

        It does make sense indeed! As Anonymous Monk has suggested, you are trying to mock DBI's objects interface: for each test case you'd like to develop you mock the relative object methods. Look at Test::MockObject for fine grained object mocking.

        Let me try to reiterate. The "mock" testing strategy would mean preparing three different mock objects, one failing each particular call you want to handle failure of and succeeding others. First you would set connect() to fail, run your code and check whether the error handling subroutines are called. Then you would restore normal behaviour of connect() (see also: DBD::Mock to allow testing database error handling without a live database) but set prepare to fail and run your code again. Lather, rinse, repeat until all error conditions you want to be able to handle are tested.