{"id":12480,"date":"2010-04-21T10:27:54","date_gmt":"2010-04-21T08:27:54","guid":{"rendered":"https:\/\/mamchenkov.net\/wordpress\/?p=12480"},"modified":"2010-04-21T10:27:54","modified_gmt":"2010-04-21T08:27:54","slug":"unit-tests-with-cakephp","status":"publish","type":"post","link":"https:\/\/mamchenkov.net\/wordpress\/2010\/04\/21\/unit-tests-with-cakephp\/","title":{"rendered":"Unit tests with CakePHP"},"content":{"rendered":"<!-- google_ad_section_start -->\n<p>I&#8217;ve spent a large part of yesterday setting up the testing environment for a CakePHP project.\u00a0 As always, every time I do something that I have done before, I wanted to do it better, using all the experienced that was acquired previously.\u00a0 And this often leads to the discovery of new things &#8211; both good and bad.\u00a0 Here is a record of what I&#8217;ve learned yesterday.<\/p>\n<p><!--more--><strong>Memory limit<\/strong><\/p>\n<p>Running &#8216;All Tests&#8217;, I kept getting an out of memory error, which was weird.\u00a0 I increased the <em>memory_limit<\/em> in the global PHP configuration file (<em>\/etc\/php.ini<\/em>), but the number of allocated bytes mentioned in the error message wasn&#8217;t changing.\u00a0 No matter how many times I restarted my web server.\u00a0 It turns out, CakePHP allows running tests with a different memory_limit value than the rest of your application.\u00a0\u00a0 You can change this value in <em>app\/webroot\/test.php<\/em> file.<\/p>\n<p>I consider this to be a very useful option.\u00a0 In some cases, unit tests might require much more memory than the actual application.\u00a0 In other cases, you&#8217;d want to limit tests&#8217; memory to see if anything weird is going on, while providing more memory for the application itself.\u00a0 Having an option to control this behaviour is A Good Thing.<\/p>\n<p><strong>Fixtures with real data<\/strong><\/p>\n<p>Previously I only used &#8216;manual&#8217; fixtures, where I was defining by hand the structure of the tables and data that goes into them &#8211; all inside a fixture.\u00a0 For my current project however it makes more sense to use data from the real database.\u00a0 One of the reasons is because we&#8217;ll probably end up using <a href=\"http:\/\/bakery.cakephp.org\/articles\/view\/soft-delete-behavior\">Soft Deletable Behaviour<\/a> for all models, which will preserve all the records in the database.<\/p>\n<p>Setting up CakePHP fixtures to automatically import data from a real database turned out to be trivial.\u00a0 All as described in <a href=\"http:\/\/book.cakephp.org\/view\/358\/Preparing-test-data\">Preparing test data<\/a> chapter of <a href=\"http:\/\/book.cakephp.org\/\">The Book<\/a>.\u00a0 Except for two little things.<\/p>\n<ol>\n<li>If you have tables with a lot of records, CakePHP will probably run out of memory when copying data to the test table.\u00a0 Point the fixture to the model in the $import array, but don&#8217;t set &#8216;records&#8217;=&gt;true.\u00a0 Better define records manually in the fixture.<\/li>\n<li>If you have any timestamp fields in your schema, you&#8217;ll probably discover that there is a bug in CakePHP that will prevent your fixtures from creating test tables.\u00a0 See below for more details.<\/li>\n<\/ol>\n<p><strong>MySQL schema generator bug<\/strong><\/p>\n<p>When fixtures were prepared and tests written and I was hoping all will run smoothly, I encountered a weird problem &#8211; a MySQL error.\u00a0 Here is what was happening:<\/p>\n<ol>\n<li>Test suite was loading fixtures.<\/li>\n<li>It saw that it had to create a table test_suite_something for model Something.<\/li>\n<li>It inspected the model Something to find which table it was using.<\/li>\n<li>It was getting table something schema from MySQL (explain something).<\/li>\n<li>It was generating an SQL query to create an identical table with a different name (test_suite_something)<\/li>\n<li>It was failing to run that SQL query due to an error in that query.<\/li>\n<\/ol>\n<p>After digging deeper into CakePHP sources, I found where the problem was.\u00a0 In file cake\/libs\/model\/datasources\/dbo_source.php there is a method buildColumn (around line 2346).\u00a0 Here is the relevant part of that method:<\/p>\n<pre class=\"brush: php; first-line: 2384; title: ; notranslate\" title=\"\">\r\n\r\n} elseif (isset($column&#x5B;'default']) &amp;&amp; isset($column&#x5B;'null']) &amp;&amp; $column&#x5B;'null'] == false) {\r\n$out .= ' DEFAULT ' . $this-&gt;value($column&#x5B;'default'], $type) . ' NOT NULL';\r\n } elseif (isset($column&#x5B;'default'])) {\r\n$out .= ' DEFAULT ' . $this-&gt;value($column&#x5B;'default'], $type);\r\n } elseif (isset($column&#x5B;'null']) &amp;&amp; $column&#x5B;'null'] == true) {\r\n$out .= ' DEFAULT NULL';\r\n } elseif (isset($column&#x5B;'null']) &amp;&amp; $column&#x5B;'null'] == false) {\r\n$out .= ' NOT NULL';\r\n }\r\n<\/pre>\n<p>The problem here is that if you have, for example, a field of type timestamp with NOT NULL constraint, MySQL will also set this field&#8217;s default value to CURRENT_TIMESTAMP.\u00a0 And CakePHP will get it from the table schema description.\u00a0 However, when it will generate the SQL query for CREATE TABLE test_suite_something, according to the code above, it will single quote the default value of CURRENT_TIMESTAMP.\u00a0 Here is how the query will look:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\n\r\nCREATE TABLE `something` (\r\n `id` int(11) NOT NULL AUTO_INCREMENT,\r\n `name` varchar(255) NOT NULL,\r\n `parent_id` int(11) DEFAULT 0 NOT NULL,\r\n `created` datetime DEFAULT NULL,\r\n `modified` timestamp DEFAULT 'CURRENT_TIMESTAMP' NOT NULL,\u00a0\u00a0 \u00a0PRIMARY KEY\u00a0 (`id`));\r\n\r\n<\/pre>\n<p>This query will generate error if you will send it to MySQL.\u00a0 And even if it worked, it would be incorrect, because CURRENT_TIMESTAMP should not be in single quotes &#8211; it&#8217;s a built-in MySQL value, not a generic string.<\/p>\n<p>Once the problem is known, the solution is usually pretty easy to do.\u00a0 I decided not to patch the method in dbo_source.php, which would require me to modify an existing method, complicate upgrades, and probably break compatibility with non-MySQL databases.\u00a0 Instead, a more elegant solution seemed to be adding a buildColumn() method to cake\/libs\/model\/datasources\/dbo\/dbo_mysql.php .\u00a0 Here is the code for the method I added:<\/p>\n<pre class=\"brush: php; first-line: 673; title: ; notranslate\" title=\"\">\r\n\r\n\/**\r\n * Generate a database-native column schema string\r\n *\r\n * All the hard work is done by the parent method. We just fix a few minor things for MySQL\r\n *\r\n * @param array $column An array structured like the following: array('name'=&gt;'value', 'type'=&gt;'value'&#x5B;, options]),\r\n *\u00a0\u00a0 where options can be 'default', 'length', or 'key'.\r\n * @return string\r\n *\/\r\n function buildColumn($column) {\r\n$result = parent::buildColumn($column);\r\n\r\n$unquotes = array('CURRENT_TIMESTAMP', 'NULL', 'NOT NULL');\r\nforeach ($unquotes as $unquote) {\r\n$result = preg_replace(&quot;\/DEFAULT\\s'{$unquote}'\/&quot;, &quot;DEFAULT {$unquote}&quot;, $result);\r\n}\r\n\r\nreturn $result;\r\n }\r\n<\/pre>\n<p>This way we call the parent::buildColumn() method and then just fix the results for MySQL.<\/p>\n<p>Once I figured out that last bit &#8211; everything just started working like a charm.\u00a0 Fixtures were importing test data, unit tests were executing, and I got a confidence boost for upcoming code changes.<\/p>\n<p>P.S.: All of the above is based on CakePHP 1.2.6 version.<\/p>\n<!-- google_ad_section_end -->\n","protected":false},"excerpt":{"rendered":"<!-- google_ad_section_start -->\n<p>I&#8217;ve spent a large part of yesterday setting up the testing environment for a CakePHP project.\u00a0 As always, every time I do something that I have done before, I wanted to do it better, using all the experienced that was acquired previously.\u00a0 And this often leads to the discovery of new things &#8211; both good &hellip; <a href=\"https:\/\/mamchenkov.net\/wordpress\/2010\/04\/21\/unit-tests-with-cakephp\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Unit tests with CakePHP<\/span><\/a><\/p>\n<!-- google_ad_section_end -->\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_links_to":"","_links_to_target":""},"categories":[1,18,62],"tags":[1537,1108],"keyring_services":[],"class_list":["post-12480","post","type-post","status-publish","format-standard","hentry","category-general","category-programming","category-technology","tag-cakephp","tag-testing"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":27886,"url":"https:\/\/mamchenkov.net\/wordpress\/2017\/08\/31\/cakephp-with-nightwatchjs-on-travis-ci\/","url_meta":{"origin":12480,"position":0},"title":"CakePHP with NightwatchJS on Travis CI","author":"Leonid Mamchenkov","date":"August 31, 2017","format":false,"excerpt":"My colleague Andrey Vystavkin has been setting up a testing environment for our CakePHP projects recently. \u00a0We had one before, of course, using PHPUnit. \u00a0But this time we wanted to add Google Chrome headless browser with some form of JavaScript test suite, so that we could cover functional tests and\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":15740,"url":"https:\/\/mamchenkov.net\/wordpress\/2011\/10\/18\/cakephp-2-0-released\/","url_meta":{"origin":12480,"position":1},"title":"CakePHP 2.0 released!","author":"Leonid Mamchenkov","date":"October 18, 2011","format":false,"excerpt":"I've been a bit all over the place these last few days, but I knew that this was coming shortly - CakePHP team released the new and much improved version 2.0 a couple of days ago. There are a lot of changes. And I do mean a lot. Here are\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":27877,"url":"https:\/\/mamchenkov.net\/wordpress\/2017\/08\/28\/integrated-package-for-better-testing-in-cakephp\/","url_meta":{"origin":12480,"position":2},"title":"Integrated Package for better testing in CakePHP","author":"Leonid Mamchenkov","date":"August 28, 2017","format":false,"excerpt":"Viraj Khatavkar wrote this blog post showing how to use Integrated Package for better testing in CakePHP. \u00a0Testing in general is not a simple subject, so anything to assist with it is very very welcome. I'm sure we'll be trying it at work in the next week or two.","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":28478,"url":"https:\/\/mamchenkov.net\/wordpress\/2018\/04\/17\/cakephp-3-6-0-release\/","url_meta":{"origin":12480,"position":3},"title":"CakePHP 3.6.0 release","author":"Leonid Mamchenkov","date":"April 17, 2018","format":false,"excerpt":"My all time favorite PHP framework - CakePHP has recently announced the availability of the long awaited version 3.6.0.\u00a0 What's so special about this particular version? - those of you not very familiar with CakePHP might ask.\u00a0 And I'll tell you. CakePHP is a well established framework, with long history\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":22103,"url":"https:\/\/mamchenkov.net\/wordpress\/2014\/07\/01\/cakephp-3-here-we-go-again\/","url_meta":{"origin":12480,"position":4},"title":"CakePHP 3, here we go again.","author":"Leonid Mamchenkov","date":"July 1, 2014","format":false,"excerpt":"As some of you might know, I'm a big fan of CakePHP framework. \u00a0I've used it on numerous projects since the beginning of times. \u00a0I've built projects small and large, migrated existing native PHP codebases to CakePHP and even survived a few major CakePHP upgrades - 1.2 to 2.0 comes\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":24276,"url":"https:\/\/mamchenkov.net\/wordpress\/2015\/06\/02\/claim-to-fame-phinx-longblob\/","url_meta":{"origin":12480,"position":5},"title":"Claim to fame : phinx LONGBLOB","author":"Leonid Mamchenkov","date":"June 2, 2015","format":false,"excerpt":"My largest claim to fame in the Open Source software just got merged in - a pull request to the phinx project, adding support for MySQL's LONGBLOB (as well as TINYBLOB and MEDIUMBLOB). \u00a0Phinx is the PHP tool for database migrations. \u00a0It's used, among others, by the CakePHP 3 framework.\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/12480","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/comments?post=12480"}],"version-history":[{"count":0,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/12480\/revisions"}],"wp:attachment":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/media?parent=12480"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/categories?post=12480"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/tags?post=12480"},{"taxonomy":"keyring_services","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/keyring_services?post=12480"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}