Database Journal
MS SQL Oracle DB2 Access MySQL PostgreSQL Sybase PHP SQL Etc SQL Scripts & Samples Links Database Forum

» Database Journal Home
» Database Articles
» Database Tutorials
MS SQL
Oracle
DB2
MS Access
MySQL
» RESOURCES
Database Tools
SQL Scripts & Samples
Links
» Database Forum
» Sitemap
Free Newsletters:
DatabaseDaily  
News Via RSS Feed


follow us on Twitter
Database Journal |DBA Support |SQLCourse |SQLCourse2
 

Featured Database Articles

MySQL

Posted August 13, 2012

Testing Your MySQL Stored Procedures with MyTAP

By Rob Gravelle

If your MySQL database interfaces with applications or is used to generate reports then chances are good that it contains some massive stored procedures, just brimming with intensive logic and calculation code.  In that case, you had better set up some type of unit testing framework so that you can verify that your SQL code is doing what it should.  This article will introduce MyTAP, a unit-test suite based on the Test Anything Protocol (TAP). It’s the brainchild of David E. Wheeler, who based his creation on the more general database testing tool called pgTAP

Installing MyTAP

MyTAP is free to download from GitHub.  To install it, just extract the archive to a folder and add the MyTap database to your MySQL server by issuing the following command:

mysql -u root < mytap.sql

That will create the tap database, which contains a couple of tables and a whole lot of stored functions.

Always Have a Plan

The main purpose of a plan is to tell MyTAP how many tests your script is going to run.

To declare a plan, call the plan() function, passing in the number of tests to run:

SELECT tap.plan( 5 );

You can calculate the number of tests, if you need to:

SELECT plan(COUNT(*)) FROM mytable;

For those times that you aren’t sure how many tests you want to run, you can call the no-arguments no_plan() method:

CALL tap.no_plan();

It’s always preferable to have a plan over not having one, so try to avoid using no_plan() as much as possible. 

Here’s a sample test script:

-- Start a transaction.
BEGIN;

-- Turn off safe updates.
SET SQL_SAFE_UPDATES=0;

-- Plan the tests.
SELECT tap.plan(1);

-- Run the tests.
-- The following variable would normally be populated
 -- from the stored proc or function that you’re testing.
SET @testvalue = 'test';
SELECT tap.ok( @testvalue LIKE 'test', '@testvalue compare' );

-- Finish the tests and clean up.
CALL tap.finish();
ROLLBACK;

Some points to keep in mind:

  • Be sure to turn off safe updates before calling any tap functions as these will fail otherwise.
  • Always provide a test name or description to the test functions so that you can identify them in the output.
  • At the end of your script, include a call to tap.finish() so that MyTAP can output diagnostics about failures and/or discrepancies between the planned number of tests and the number actually run.

To run the above test, save it to a .sql file and execute it from the mysql command line interface:

E:\ >mysql -u root --batch --raw --skip-column-names --unbuffered --database company < testscript.sql

1..1
ok 1 - @testvalue compare

Although the MyTAP docs include the use of the --execute flag before the .sql file, it produced errors for me.  For MySQL 5.5, the ‘<’ redirection symbol produced the desired result.

Canned Tests

The purpose of any testing framework is to match some calculated result against a predefined expectation.  MyTap includes several standard test functions that accommodate different kinds of comparisons against various data types.

ok()

The simplest of the predefined tests is the ok() function. It accepts an expression that evaluates to either true or false; a value of true signifies that the test succeeded, while false means that it failed:

ok( boolean, description )

Here are a few examples:

SELECT tap.ok( 9 < 10,        'numeric comparison' );
SELECT tap.ok( 'pie' ~ '^p',  'regex comparison' );
SELECT tap.ok( London,        concat(name, ' city London' ))
FROM cities;

On failure, ok() will produce some diagnostics:

SELECT tap.ok( @retval < 10, 'numeric comparison' );

not ok 1 - numeric comparison
#     Failed test 1: "numeric comparison"

Moreover, a NULL calculated value will automatically fail the test and append an additional diagnostic:

not ok 1 - numeric comparison
#     Failed test 1: "numeric comparison"
#     (test result was NULL)

eq() and not_eq()

If ok() compares an expression to "== true", then eq() and not_eq() compare their two arguments using "=" and "<>", respectively, to determine whether the test succeeds or fails:

SELECT tap.eq( my_func(), 1, 'my_func_eq_test' );

SELECT tap.not_eq( get_id(), '', 'get_id() not empty string test' );

While the same results can be achieved using the ok() function, eq() and not_eq() produce better diagnostics on failure because, unlike ok(), eq() and not_eq() can distinguish between the two individual values.  These are thus reported in the diagnostics:

SELECT tap.eq( 'Audi', 'Corvette', 'Car comparison test' );

Will produce something like this:

# Failed test 7:  "Car comparison test"
#         have: Audi
#         want: Corvette

Similar to eq() and not_eq(), matches() and doesnt_match() compares @this to that using a regex (i.e., /^that/):

SELECT matches( @this, '^that', @description );
SELECT doesnt_match( @this, '^that', @description );

For simple pattern matching, alike() matches a test value against the SQL LIKE pattern matcher, while unalike() matches a test value against NOT LIKE:

SELECT alike( @this, 'that%', @description );
SELECT unalike( @this, 'that%', @description );

Schema Tests

MyTAP also contains tests for checking your database schema.  One such test is called has_table():

has_table( database, table, description )

This function tests whether or not a table exists in a database. If you want to test for a table in the current database, the DATABASE() function will do the trick:

SELECT has_table(DATABASE(), 'cars', 'Check for table cars');

Writing Your Own Tests

All MyTAP tests are written as MySQL functions, so it’s really quite easy to add your own.  Here’s a test that checks for empty values:

DROP FUNCTION IF EXITS is_empty
DELIMITER //
CREATE FUNCTION is_empty (test_string TEXT, descr TEXT)
RETURNS TEXT
BEGIN
    IF test_string IS NULL || LENGTH(TRIM(test_string)) = 0 THEN
        RETURN ok(1, descr);
    END IF;
    RETURN concat(ok( 0, descr ), '\n', diag(concat(
           ' supplied string was not empty.'
    )));
END //

DELIMITER ;

Calling MyTAP’s ok() function – or any function that ultimately calls ok() - guarantees that the output is properly formatted and recorded in the database for diagnostics at the end of the test script. You can also provide your own diagnostics by appending them to ok()’s output  as done above.

Conclusion

One of the things that I really like about MyTAP is that you don’t need any additional software or tools to run it; everything is contained in the tap MySQL database. But that doesn’t mean that there aren’t any other tools out there.  In fact, there is a TAP Harness called my_prove that apparently simplifies running the tests from the command line.  I’m considering giving it a try in the near future.

See all articles by Rob Gravelle



MySQL Archives

Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 




Latest Forum Threads
MySQL Forum
Topic By Replies Updated
MySQL in high availability and transction secure banking application klamor 1 August 28th, 10:24 AM
MySQL rollback UAL225 1 August 28th, 10:15 AM
Browsing a DB file that uses MySql finleytech 1 July 26th, 10:51 AM
php cookie won't pass variable to next page ITdevGirl 0 June 22nd, 12:13 PM


















Thanks for your registration, follow us on our social networks to keep up-to-date