utMySQL
utMySQL is a unit test frame work for MySQL V 5 using stored procedures.
utMysql is an Open Source project, using the GNU General Public License (GPL) and is operating system independent.
Downloads are located on the sourceforge project page: http://sourceforge.net/projects/utmysql/
The utMySQL home page is located here: http://utmysql.sourceforge.net/
Stored procedure in MySQL V5.0 are very useful in removing the database query layer from server side scripting. It is entirely possible to develop and maintain the database layer separately from the server side code.
Installation
Test harness
Additional notes
Install the example.
To install unit testing for MySQL run the 'utMySQL_pkg_TestSuite.sql' script file in
your chosen database (make sure you have the correct privileges for creating
stored procedures and that you ARE actually in a database). Four procedure are installed by utMySQL:
utMySQL_pkg_TestSuite()
This stored procedure will look for ALL procedures (not functions) in the
current database schema (expect for MySQL V5.0.0 where procedures
are considered global to ALL databases) that start with the the letters
"ut_" and execute them as unit tests cases under the utMySQL framework.
utMySQL_pkg_SelfTest()
This procedure executes transparently prior to ANY test cases being executed, it checks that utMySQL is working as expected.
In the event of an assertion failure during this process utMySQL will stop, none of the test cases will be executed.
utAssert_eq()
An assertion procedure testing for equality between the expected result and the actual result, in the following form:
CALL utAssert_eq( sAssertionMessage, xExpectedResult, xActualResult );E.g.
CALL utAssert_eq( 'Check that user name is "BADGER"', 'BADGER', UPPER( 'badger' ) );If the assertion fails an error message is display.
utAssert_notEq()
An assertion procedure testing for non-equality between the expected result and the actual result, in the following form:
CALL utAssert_notEq( sAssertionMessage, xExpectedResult, xActualResult );E.g.
CALL utAssert_notEq( 'Check that a Badger is not a Weasel', 'Badger', 'Weasel' );If the assertion fails an error message is display.
utMySQL_pkg_TestSuiteWhere utMySQL is the package name and TestSuite the actual procedure name. This may at first glance appear wasteful (why should I have to type so much!), however the stored procedure name space soon becomes crowded and difficult to manage without. Hopefully future versions of MySQL will have support for:
Navigate to the "utMySQL/example" directory from the command prompt and start the MySql Monitor. Create a new database in MySQL, making sure you have create procedure privileges.
mysql> create database utmysql_test; Query OK, 1 row affected (0.02 sec) mysql> use utmysql_test; Database changed mysql> show tables; Empty set (0.00 sec)From the MySQL Monitor run the "full_install.sql" script located in the "install" directory. This will produce the following output:
mysql> source install/full_install.sql +---------------------------------+ | Started | +---------------------------------+ | Creating utMySQL example tables | +---------------------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) +------------------------------+ | Started | +------------------------------+ | Installing example packages. | +------------------------------+ 1 row in set (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.01 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.01 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) +-------------------------------+ | Started | +-------------------------------+ | Installing example unit tests | +-------------------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) +--------------------+ | Started | +--------------------+ | Installing utMySQL | +--------------------+ 1 row in set (0.01 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) +-------------------------+ | Tables_in_utmysql_test | +-------------------------+ | article | | article_audit_trail | | author | | configuration_parameter | +-------------------------+ 4 rows in set (0.00 sec) +-----------------------------------------------+--------------+ | routine_name | routine_type | +-----------------------------------------------+--------------+ | Article_pkg_addArticle | FUNCTION | | Article_pkg_createArticleAudit | FUNCTION | | Article_pkg_getArticles | PROCEDURE | | Author_pkg_addAuthor | FUNCTION | | Author_pkg_getAuthors | PROCEDURE | | Configuration_pkg_getConfigurationParamter | FUNCTION | | Configuration_pkg_setConfigurationParamter | PROCEDURE | | ManageSchema_pkg_articleAuditTrailData | PROCEDURE | | ManageSchema_pkg_articleData | PROCEDURE | | ManageSchema_pkg_authorData | PROCEDURE | | ManageSchema_pkg_clearSchema | PROCEDURE | | ManageSchema_pkg_configurationParamterData | PROCEDURE | | ManageSchema_pkg_installUnitTestData | PROCEDURE | | utAssert_eq | PROCEDURE | | utAssert_notEq | PROCEDURE | | utMySQL_pkg_SelfTest | PROCEDURE | | utMySQL_pkg_TestSuite | PROCEDURE | | ut_Article_pkg_addArticle | PROCEDURE | | ut_Article_pkg_createArticleAudit | PROCEDURE | | ut_Article_pkg_getArticles | PROCEDURE | | ut_Author_pkg_addAuthor | PROCEDURE | | ut_Author_pkg_getAuthors | PROCEDURE | | ut_Configuration_pkg_getConfigurationParamter | PROCEDURE | | ut_Configuration_pkg_setConfigurationParamter | PROCEDURE | +-----------------------------------------------+--------------+ 24 rows in set (0.02 sec) mysql>
Once run successfully this will create:
To run a single test case in isolation:
mysql> CALL ut_Article_pkg_getArticles(); Query OK, 0 rows affected (0.14 sec)The fact that no assertion messages appear indicate that the tests have all passed. To cause an assertion failure let us change the expected result of a test in the ut_Article_pkg. Changing the assertion 'Check first article in list, title field.' in the getArticles() test case:
CALL utAssert_eq( 'Check first article in list, title field.' , 'CHEESE shortage looms' , sTitle );To:
CALL utAssert_eq( 'Check first article in list, title field.' , 'Gasp!' , sTitle );Save and recompile the script by:
mysql> source tests/ut_Article_pkg.sql Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected (0.00 sec)Run test case again:
mysql> CALL ut_Article_pkg_getArticles(); +----------------+-------------------------------------------+-----------------+-----------------------+ | Unit Test Name | utAssert_eq Message | Expected Result | Actual Result | +----------------+-------------------------------------------+-----------------+-----------------------+ | NULL | Check first article in list, title field. | Gasp! | CHEESE shortage looms | +----------------+-------------------------------------------+-----------------+-----------------------+ 1 row in set (0.13 sec) Query OK, 0 rows affected, 1 warning (0.16 sec)This time we can see that an Assertion has occurred... Four pieces of information are display:
To run a the entire test suite:
mysql> CALL utMySQL_pkg_TestSuite( ); +------------------------------------+ | Starting MySQL Test Suite | +------------------------------------+ | utmysql_test : 2006-10-20 22:39:13 | +------------------------------------+ 1 row in set (0.02 sec) +---------------------------+ | Running unit test: | +---------------------------+ | ut_Article_pkg_addArticle | +---------------------------+ 1 row in set (0.05 sec) +-----------------------------------+ | Running unit test: | +-----------------------------------+ | ut_Article_pkg_createArticleAudit | +-----------------------------------+ 1 row in set (0.22 sec) +----------------------------+ | Running unit test: | +----------------------------+ | ut_Article_pkg_getArticles | +----------------------------+ 1 row in set (0.33 sec) +------------------------------+-------------------------------------------+-----------------+-----------------------+ | Unit Test Name | utAssert_eq Message | Expected Result | Actual Result | +------------------------------+-------------------------------------------+-----------------+-----------------------+ | ut_Article_pkg_getArticles() | Check first article in list, title field. | Gasp! | CHEESE shortage looms | +------------------------------+-------------------------------------------+-----------------+-----------------------+ 1 row in set (0.36 sec) +-------------------------+ | Running unit test: | +-------------------------+ | ut_Author_pkg_addAuthor | +-------------------------+ 1 row in set (0.38 sec) +--------------------------+ | Running unit test: | +--------------------------+ | ut_Author_pkg_getAuthors | +--------------------------+ 1 row in set (0.39 sec) +-----------------------------------------------+ | Running unit test: | +-----------------------------------------------+ | ut_Configuration_pkg_getConfigurationParamter | +-----------------------------------------------+ 1 row in set (0.41 sec) +-----------------------------------------------+ | Running unit test: | +-----------------------------------------------+ | ut_Configuration_pkg_setConfigurationParamter | +-----------------------------------------------+ 1 row in set (0.42 sec) +-------------------+--------------------+ | Total Assertions: | Assertions Failed: | +-------------------+--------------------+ | 105 | 1 | +-------------------+--------------------+ 1 row in set (0.44 sec) +-------------+ | Time Taken: | +-------------+ | 1s | +-------------+ 1 row in set (0.48 sec) Query OK, 0 rows affected (0.50 sec)As you can see it indicates which database the tests where started in, what time it started, which unit tests have started running and that our assertion is still failing (because we haven't fixed it yet). At the bottom is a total of the number of assertions and if any failed assertions:
+-------------------+--------------------+ | Total Assertions: | Assertions Failed: | +-------------------+--------------------+ | 105 | 1 | +-------------------+--------------------+Time to fix the assertion in ut_Article_pkg_getArticles(), recompile and run the test suite again:
mysql> CALL utMySQL_pkg_TestSuite( ); +------------------------------------+ | Starting MySQL Test Suite | +------------------------------------+ | utmysql_test : 2006-10-20 22:42:25 | +------------------------------------+ 1 row in set (0.01 sec) +---------------------------+ | Running unit test: | +---------------------------+ | ut_Article_pkg_addArticle | +---------------------------+ 1 row in set (0.05 sec) +-----------------------------------+ | Running unit test: | +-----------------------------------+ | ut_Article_pkg_createArticleAudit | +-----------------------------------+ 1 row in set (0.19 sec) +----------------------------+ | Running unit test: | +----------------------------+ | ut_Article_pkg_getArticles | +----------------------------+ 1 row in set (0.31 sec) +-------------------------+ | Running unit test: | +-------------------------+ | ut_Author_pkg_addAuthor | +-------------------------+ 1 row in set (0.36 sec) +--------------------------+ | Running unit test: | +--------------------------+ | ut_Author_pkg_getAuthors | +--------------------------+ 1 row in set (0.38 sec) +-----------------------------------------------+ | Running unit test: | +-----------------------------------------------+ | ut_Configuration_pkg_getConfigurationParamter | +-----------------------------------------------+ 1 row in set (0.39 sec) +-----------------------------------------------+ | Running unit test: | +-----------------------------------------------+ | ut_Configuration_pkg_setConfigurationParamter | +-----------------------------------------------+ 1 row in set (0.41 sec) +-------------------+--------------------+ | Total Assertions: | Assertions Failed: | +-------------------+--------------------+ | 105 | 0 | +-------------------+--------------------+ 1 row in set (0.42 sec) +-------------+ | Time Taken: | +-------------+ | 0s | +-------------+ 1 row in set (0.42 sec)This time no failures. (You did save and recompile right?)
OK the tests passed, so what!?
In rounding off this example, all the tests have passed - now what? The tests have passed
our expected out comes. This, of course, does not mean there are no errors
in the code. If errors are found new tests should be written to expose them, then fix the code.
To actually use the functionality created firstly we need to call the SchemaManager
to clear the database and install some useful test data. Obviously we would not
expect to see or install the SchemaManager in a live environment,
but as this is a test environment it is present.
mysql> CALL SchemaManager_pkg_clearSchema(); Query OK, 0 rows affected (0.02 sec) mysql> CALL SchemaManager_pkg_installUnitTestData(); Query OK, 1 row affected (0.09 sec)Calling these two procedures first clears all the tables in the schema, the second installs some test data.
mysql> CALL Article_pkg_getArticles( FALSE ); +---------------------------------+---------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------------+----------+-------------------+ | title | summary | text | sticky | dateCreated | language | authorDisplayName | +---------------------------------+---------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------------+----------+-------------------+ | CHEESE shortage looms | CHEESE shortage hits Hastings, citizens panic. | Offical say a CHEESE shortage is to engulf the town of Hastings, citizens roam streets in mobs. | 0 | 2006-09-13 13:15:16 | English | Badg | | Weasel seen looting Crypt | A weasel was seen breaking into The Crypt. | An mysterious (and unknown) Weasel was seen breaking and entering into a local pub The Crypt in Battle. No items were stolen, except for the kitchens cheese supplies. crumbs where found, owners and local Police baffled.. | 1 | 2006-09-13 13:14:15 | English | Weas | | Badger on Cheese eating rampage | Badger goes bezerk and eats all the cheese in Hastings. | An unknown Badger went bezerk today in Hastings, eating all the cheese. Some witnesses suggest he was wearing a mask.. | 0 | 2006-09-13 13:13:13 | English | Badg | +---------------------------------+---------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------------+----------+-------------------+ 3 rows in set (0.02 sec) Query OK, 0 rows affected (0.05 sec)To list all the authors:
mysql> CALL Author_pkg_getAuthors( FALSE ); +------+------------+-------------+--------------------+ | id | name | displayName | emailAddress | +------+------------+-------------+--------------------+ | 1 | Mr. Badger | Badg | Badger@example.com | | 2 | Mr. Weasel | Weas | Weasel@example.com | | 3 | The Editor | [Ed] | Editor@example.com | +------+------------+-------------+--------------------+ 3 rows in set (0.00 sec) Query OK, 0 rows affected (0.00 sec)All queries have now been removed from server side scripts and can be modified and tested independently. It may be tempting to call these stored procedures directly from the server side scripts, however it is best to introduce another layer, server objects, which are the only objects that connect to the database and feed the data into the server side scripts. This has three immediate benefits:
Unit Testing for MySQL (V 1.0.1) by Warren Willmey 2006