{"id":188,"date":"2022-10-06T16:25:42","date_gmt":"2022-10-06T20:25:42","guid":{"rendered":"https:\/\/serioussemicolons.wordpress.com\/?p=188"},"modified":"2022-10-06T16:25:42","modified_gmt":"2022-10-06T20:25:42","slug":"phpunit-tips-for-debugging-tests-that-can-time-travel","status":"publish","type":"post","link":"https:\/\/blog.bryant.ai\/?p=188","title":{"rendered":"PHPUnit tips for debugging tests that can time travel"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I had to go nine rounds recently with a really elusive test situation where certain tests would fail downstream, but only if certain OTHER tests were enabled and running in the test suite. This also meant that these same tests, failing in every other run, would pass when run by themselves. They also (for fun I guess) would pass <em>perfectly<\/em> in the CI\/CD pipeline but not on dev, but only occasionally. It felt like the tests were hopping in a TARDIS and time-traveling back and forth to pass-land and fail-land right in my terminal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>ARRRRRRRRRRRRRGGGGGGGGGGGGGGGHHHHHHHHHHH!!!!!!!!<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes our test suites can make us feel like that. But, we love them anyways because they take care of us and keep us from shipping bad code, so we continue to tolerate them. In my particular case, though, the work was made less painful by my good fortune to have a very well-informed colleague helping me debug. He taught me three neat tricks that helped speed the process along and get us to a solution:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tip 1: Create custom test suites, and quarantine the bad tests in a separate suite<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This one was so simple I couldn&#8217;t believe I hadn&#8217;t thought of it, but <em>you don&#8217;t need to have all your tests in one test suite<\/em>. Create a separate suite for just the bad ones, and then examine them one by one until you find the issue.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!-- the whole enchilada: not helpful --&gt;\n&lt;testsuite name=\"app\"&gt;\n    &lt;directory&gt;tests\/TestCase\/&lt;\/directory&gt;\n&lt;\/testsuite&gt;\n\n&lt;!-- just the bad tests, and whatever else you want --&gt;\n&lt;testsuite name=\"debug\"&gt;\n    &lt;file&gt;tests\/TestCase\/FailingTestClassA.php&lt;\/file&gt;\n    &lt;file&gt;tests\/TestCase\/FailingTestClassB.php&lt;\/file&gt;\n    &lt;file&gt;tests\/TestCase\/FailingTestClassC.php&lt;\/file&gt;\n&lt;\/testsuite&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">&#8230;and then run just that suite:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$&gt; vendor\/bin\/phpunit --testsuite debug<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is also helpful for when you know you&#8217;re going to be running the same tests over and over again (like when doing TDD) and you don&#8217;t want to run the <em>entire<\/em> app test suite each go. Careful, though, because you might not be informed when you break something downstream.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tip 2: Run your test suite <em>n <\/em>times to discover larger patterns that were previously hidden<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I didn&#8217;t even know PHPUnit could do this, but it makes so much sense:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$&gt; vendor\/bin\/phpunit --testsuite asdf --repeat 10<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will run the asdf test suite 10 times. This came in SUPER handy when I was debugging tests that would pass sometimes, fail sometimes (database race conditions, yay&#8230;.). By just running the test in a single pass one at a time, I was missing larger patterns that became apparent when running it 10 times, then 100, then 1000&#8230;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Sidebar: I am blessed that my app&#8217;s test suite currently runs in 4 seconds, so that last run may have taken a little over an hour, but at least it wasn&#8217;t four weeks like some projects.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tip 3: Experiment with some of the other formats<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most people just use the default PHPUnit output format and never look at any of the other pretty ones. Check out the clean looks of <code>--testdox<\/code>!!!:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># (if your terminal supports ANSI, try it with --colors)\nApplication (App\\Test\\TestCase\\Application)\n \u2714 Bootstrap loads correct plugins\n \u2714 Cli bootstrap loads correct plugins\n \u2714 Bootstrap loads correct plugins in debug mode\n \u2714 Cli bootstrap loads correct plugins in debug mode\n \u2714 Bootstrap does not halt when exceptions occur\n \u2714 Bootstrap loads correct middlewares\n \u2714 Bootstrap loads correct config keys\n\n# skipped tests look like this\nSkipped Class (App\\Test\\TestCase\\Foo\\SkippedController)\n \u2205 My skipped test<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">How extremely, extremely readable. Also, if you practice good test naming schema, the output is very human-friendly. This output also quickly highlights which areas of your test suite have tests that you haven&#8217;t implemented yet or just marked incomplete\/skipped <s>because you&#8217;re lazy<\/s> <s>because testing is very hard and I don&#8217;t wanna do it anymore<\/s> because the release is due \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bonus tip for PhpStorm users: Run History<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I was also taught this which I found delightful: in the <strong><em>Run <\/em><\/strong>tool in PhpStorm there is a clock icon on the left-side-middle of the pane called Test History (screenshot below):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"840\" height=\"530\" src=\"https:\/\/blog.bryant.ai\/wp-content\/uploads\/2022\/10\/scr-20221006-mcs.png?w=840\" alt=\"\" class=\"wp-image-201\" srcset=\"https:\/\/blog.bryant.ai\/wp-content\/uploads\/2022\/10\/scr-20221006-mcs.png 840w, https:\/\/blog.bryant.ai\/wp-content\/uploads\/2022\/10\/scr-20221006-mcs-300x189.png 300w, https:\/\/blog.bryant.ai\/wp-content\/uploads\/2022\/10\/scr-20221006-mcs-768x485.png 768w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you click it, you&#8217;ll get a dropdown of your last 10-15 test runs and you can <em>pull up those results for quick inspection<\/em>. It&#8217;s REALLY handy for seeing your test run results over time, and the menu is also filterable, so you can type &#8220;my-run-config-name&#8221; to filter the entries down to just your preferred run configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s it for the tips, thanks for reading. I found these to be very helpful for me in my recent work with PHPUnit 9 in uncovering a really nasty series of bugs&#8230;hope you find them useful in your work as well. Contribute to Open Source!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&lt;!\u2013 wp:paragraph \u2013&gt;<br \/>\n&lt;p&gt;I had to go nine rounds recently with a really elusive test situation where certain tests would fail downstream, but only if certain OTHER tests were enabled and running in the test suite. This also meant that these same tests, failing in every other run, would pass when run by themselves. They also [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,7],"tags":[31,32,33,41,42,44,45],"class_list":["post-188","post","type-post","status-publish","format-standard","hentry","category-testing","category-unit-testing","tag-php-tests","tag-phpstorm","tag-phpunit","tag-test-suites","tag-testdox","tag-tests","tag-unit-tests"],"_links":{"self":[{"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=\/wp\/v2\/posts\/188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=188"}],"version-history":[{"count":0,"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=\/wp\/v2\/posts\/188\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bryant.ai\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}