{"id":93,"date":"2006-11-25T11:49:52","date_gmt":"2006-11-25T01:49:52","guid":{"rendered":"http:\/\/www.thunderguy.com\/semicolon\/2006\/11\/25\/test-driven-development-of-wordpress-plugins\/"},"modified":"2006-11-25T11:49:52","modified_gmt":"2006-11-25T01:49:52","slug":"test-driven-development-of-wordpress-plugins","status":"publish","type":"post","link":"https:\/\/thunderguy.com\/semicolon\/2006\/11\/25\/test-driven-development-of-wordpress-plugins\/","title":{"rendered":"Test-driven development of WordPress plugins"},"content":{"rendered":"<p>Evermore, one of my WordPress plugins, has been around for a year or so. It&#8217;s worked very well, but occasionally I did receive reports of mysterious failures. In an effort to fix it once and for all, I decided to take a new approach. So for the last couple of updates I have used a test-driven development (TDD) approach to iron out all remaining wrinkles. The results were interesting.<\/p>\n<p>The heart of Evermore is the algorithm it uses to detect where a paragraph ends and another begins. It&#8217;s simplest to do this before WordPress reformats the paragraphs, because then Evermore can take advantage of WordPress&#8217;s tag-balancing and other functions. (For example, Evermore can break a post in the middle of a <code>&lt;div&gt;<\/code>, and WordPress will insert a <code>&lt;\/div&gt;<\/code> in the right place.) However, this makes it tricky to find paragraph breaks. For example, a double linebreak delimits a paragraph, but Evermore also allows a double <code>&lt;br\/&gt;<\/code>. Also, a paragraph ends whenever another begins (for example, with a <code>&lt;p&gt;<\/code>).<\/p>\n<p>In case all this sounds a bit simple, here is the regular expression Evermore would use to find the end of the second paragraph of a post.<\/p>\n<pre class=\"code\">\r\n<code>!^(\\s*.+?(?:(?:(?:\\r\\n *\\r\\n|\\r *\\r|\\n *\\n|&lt;br\\s*\/?&gt;\\s*&lt;br\\s\r\n*\/?&gt;)|&lt;\/(?:p|pre|blockquote|div|ol|ul|h[1-6]|table)&gt;|(?&lt;=\\W)\r\n(?=\\s*&lt;(?:p|pre|blockquote|div|ol|ul|h[1-6]|table)\\W))\\s*.+?\r\n){1})((?:(?:\\r\\n *\\r\\n|\\r *\\r|\\n *\\n|&lt;br\\s*\/?&gt;\\s*&lt;br\\s*\/?&gt;)|\r\n&lt;\/(?:p|pre|blockquote|div|ol|ul|h[1-6]|table)&gt;|(?&lt;=\\W)(?=\\s*\r\n&lt;(?:p|pre|blockquote|div|ol|ul|h[1-6]|table)\\W)))\\s*\\S!is<\/code>\r\n<\/pre>\n<p>This kind of thing is hard to test manually &#8212; there are so many possible situations. So I wrote an automated test suite. This contains a number of different snippets of post content (paragraphs separated by <code>&lt;p&gt;<\/code> tags, paragraphs separated by newlines, tables embedded in paragraphs, DOS and Unix line breaks and so on). The test harness takes every possible combination of these, and runs them through Evermore with each possible combination of options. In total, it tests 536 test cases.<\/p>\n<p>When I started this test approach, Evermore 1.0.1 was the current version. It worked pretty well except in a few obscure cases. But there are a lot of different obscure cases &#8212; Evermore 1.0.1 scores a miserable 6.7% pass rate in the test suite: 36 out of 536. I released Evermore 2.0 shortly after I started working on the test suite, and it has a much better pass rate of 28.4% (152\/536).<\/p>\n<p>After 2.0, I focused on passing all the tests. Evermore 2.1 and 2.2 (the latest) both have a nice 100% pass rate. I&#8217;m pretty confident that Evermore can handle not just every case I can think of, but <em>every combination<\/em> of every case I can think of. Of course, I haven&#8217;t thought of everything; and when a new bug comes to light, I will first expand my test suite to cover it, and then modify Evermore until it passes 100% of the new suite. Then I will know that not only I have fixed the new bug, but I haven&#8217;t re-introduced any old ones.<\/p>\n<p>I&#8217;m using this approach in other plugin development work too. In fact, I&#8217;ve had to use it to help update the Code Markup plugin that i use on Semicolon, because Code Markup wasn&#8217;t able to correctly display the regular expression in this post! So you can expect a new release of Code Markup soon. TDD is a wonderful thing. (And so are regular expressions.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Evermore, one of my WordPress plugins, has been around for a year or so. It&#8217;s worked very well, but occasionally I did receive reports of mysterious failures. In an effort to fix it once and for all, I decided to take a new approach. So for the last couple of updates I have used a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[7,83],"class_list":["post-93","post","type-post","status-publish","format-standard","hentry","category-wordpress","tag-testing","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/posts\/93","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/comments?post=93"}],"version-history":[{"count":0,"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/posts\/93\/revisions"}],"wp:attachment":[{"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/media?parent=93"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/categories?post=93"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thunderguy.com\/semicolon\/wp-json\/wp\/v2\/tags?post=93"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}