tag:codewright.biz,2008:/posts Just Wright 2011-08-17T11:58:11Z Enki CodeCraft IT jimmy@codecraftit.biz tag:codewright.biz,2008:Post/2 2011-09-07T12:08:52Z 2011-08-17T11:58:11Z Test Driven Development in Java <p>I&#8217;ve recently been looking a bit deeper into Test Driven development (<span class="caps">TDD</span>). You know, where you write a unit test first, and then write the code to fix it. I&#8217;ve been really impressed with how clean the code is that I&#8217;ve written this way, particularly in terms of its simplicity and low coupling. That got me looking for a testing framework for a Rails website I&#8217;m working on, and there I came across <a href="http://cukes.info/">Cucumber.</a> Cucumber is a Behaviour Driven Development ( <a href="http://behaviour-driven.org/"><span class="caps">BDD</span></a> ) testing tool.</p> <p>One of the principles of <span class="caps">BDD</span> is &#8220;Business and Technology should refer to the same system in the same way&#8221;, which to my limited interpretation means that business analysts should be able to read test cases.</p> <p>What is really cool about cucumber is that it allows you to specify scenarios in English (or French, Sotho, etc).</p> <p>Consider this example:<br /> <span style="color:green;">Scenario:</span> Admin users should be able to edit all profiles<br /> <span style="color:green;">Given</span> a website user Admin who is an administrator<br /> <span style="color:green;">And</span> a website user Joe who is a user<br /> <span style="color:green;">When</span> a user logs in as Admin<br /> <span style="color:green;">And</span> navigates to the User Profile page<br /> <span style="color:green;">Then</span> the page should allow editing of Joe&#8217;s profile<br /> <span style="color:green;">And</span> the page should allow editing of Admin&#8217;s profile</p> <p><span style="color:green;">Scenario:</span> Normal users should only be able to edit their own profiles<br /> <span style="color:green;">Given</span> a website user Admin who is an administrator<br /> <span style="color:green;">And</span> a website user Joe who is a user<br /> <span style="color:green;">When</span> a user logs in as Joe<br /> <span style="color:green;">And</span> navigates to the User Profile page<br /> <span style="color:green;">Then</span> the page should allow editing of Joe&#8217;s profile<br /> <span style="color:green;">And</span> the page should not allow editing of Admin&#8217;s profile</p> <p>Pretty simple right? You can read it and know exactly what the system should do under a given scenario. There&#8217;s nothing technical there at all. In fact, the only required syntax for Cucumber are the bits in green, which must start each line. You must have a Scenario, Given, When, Then. You can have as many And&#8217;s as you need under each section to specify additional Given&#8217;s/When&#8217;s/Then&#8217;s. This structure also forces a very clear, and definite way of thinking about a problem onto you – if you can&#8217;t specify it in this format (Given, When, Then) then you can&#8217;t test it (with Cucumber ;)!</p> <p>Obviously there&#8217;s a chunk of work that needs to happen behind the scenes to make these scenarios run – we&#8217;ll get to that later. What&#8217;s got me excited is that this seems a very approachable ‘language&#8217; for non-technical people to read (or in my more optimistic phases, even write) and understand.</p> <p>Imagine a project where the business analysts write scenarios like these, and have a dashboard of what scenarios are working, and which not. And developers spend their time making these tests pass, safe in the knowledge that once all the scenarios pass, their work complies with the spec, and is working! Of course that&#8217;s Utopia, but Cucumber is a step in that direction. Even if it only goes as far as allowing business analysts to read what has actually been tested and translated into functionality from scenarios which are written by developers, I think it would still add a lot of value to the communication chasm we so often find between the people who define what they want, and the people who&#8217;s job&#8217;s it is to try deliver it to them.</p> <p>Now Cucumber comes from the Ruby on Rails world, and there are some great plugins for it to simulate browsers, and do real integration level testing. There&#8217;s also a plugin that allows you to run it (via JRuby) in the <span class="caps">JVM</span>, and test Java code using Cuke4Duke, and you can do web integration testing using WebDriver/Selenium. I can&#8217;t claim to be an expert on any of this stack (or even Cucumber yet for that matter), but here&#8217;s an description of how to set up the classic calculator example to whet your appetite.</p> <h2>Install Maven</h2> <p>I have hangups against Maven from previous projects, but lets not get into that. You can also use Ant and Ivy, but the Maven way is much easier to explain.</p> <p>Download <a href="http://maven.apache.org/download.html">Maven</a> (Version 3.0.2 worked fine for me) and extract it somewhere.<br /> Add the bin folder to your path<br /> If you&#8217;re behind a proxy, set your <code>~/.m2/settings.xml</code> as described <a href="http://maven.apache.org/guides/mini/guide-proxies.html">here</a></p> <h2>Install Cucumber &amp; Cuke4Duke</h2> <p>Thanks to:</p> <p><a href="https://github.com/aslakhellesoy/cuke4duke/wiki/Maven">Cuke4Duke Maven config</a><br /> <a href="https://github.com/aslakhellesoy/cuke4duke/blob/master/examples/java/pom.xml">Cuke4Duke pom.xml example</a><br /> <a href="http://www.goodercode.com/wp/using-cucumber-tests-with-maven-and-java/">Using Cucumber tests with Maven and Java</a></p> <ol> <li>Create a working directory for this example, and add the following pom.xml to it.</li> </ol><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt>18<tt> </tt>19<tt> </tt><strong>20</strong><tt> </tt>21<tt> </tt>22<tt> </tt>23<tt> </tt>24<tt> </tt>25<tt> </tt>26<tt> </tt>27<tt> </tt>28<tt> </tt>29<tt> </tt><strong>30</strong><tt> </tt>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt>41<tt> </tt>42<tt> </tt>43<tt> </tt>44<tt> </tt>45<tt> </tt>46<tt> </tt>47<tt> </tt>48<tt> </tt>49<tt> </tt><strong>50</strong><tt> </tt>51<tt> </tt>52<tt> </tt>53<tt> </tt>54<tt> </tt>55<tt> </tt>56<tt> </tt>57<tt> </tt>58<tt> </tt>59<tt> </tt><strong>60</strong><tt> </tt>61<tt> </tt>62<tt> </tt>63<tt> </tt>64<tt> </tt>65<tt> </tt>66<tt> </tt>67<tt> </tt>68<tt> </tt>69<tt> </tt><strong>70</strong><tt> </tt>71<tt> </tt>72<tt> </tt>73<tt> </tt>74<tt> </tt>75<tt> </tt>76<tt> </tt>77<tt> </tt>78<tt> </tt>79<tt> </tt><strong>80</strong><tt> </tt>81<tt> </tt>82<tt> </tt>83<tt> </tt>84<tt> </tt>85<tt> </tt>86<tt> </tt>87<tt> </tt>88<tt> </tt>89<tt> </tt><strong>90</strong><tt> </tt>91<tt> </tt>92<tt> </tt>93<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;<tt> </tt> xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;<tt> </tt> xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0<tt> </tt> http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;<tt> </tt> &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;<tt> </tt> &lt;groupId&gt;guruhut&lt;/groupId&gt;<tt> </tt> &lt;artifactId&gt;cuke4duke-java-example&lt;/artifactId&gt;<tt> </tt> &lt;version&gt;0.0.1&lt;/version&gt;<tt> </tt> &lt;packaging&gt;jar&lt;/packaging&gt;<tt> </tt> &lt;name&gt;Cucumber for Java: Example&lt;/name&gt;<tt> </tt>&lt;properties&gt;<tt> </tt> &lt;cuke4duke.version&gt;0.4.3&lt;/cuke4duke.version&gt;<tt> </tt> &lt;!-- Behind a proxy? --&gt;<tt> </tt> &lt;!-- see http://wiki.github.com/aslakhellesoy/cuke4duke/installing-gems --&gt;<tt> </tt> &lt;!-- and http://github.com/aslakhellesoy/cuke4duke/issues/issue/36 --&gt;<tt> </tt> &lt;http.proxy&gt;http://localhost:9999&lt;/http.proxy&gt;<tt> </tt>&lt;/properties&gt;<tt> </tt> &lt;repositories&gt;<tt> </tt> &lt;repository&gt;<tt> </tt> &lt;id&gt;codehaus&lt;/id&gt;<tt> </tt> &lt;url&gt;http://repository.codehaus.org&lt;/url&gt;<tt> </tt> &lt;/repository&gt;<tt> </tt> &lt;repository&gt;<tt> </tt> &lt;id&gt;cukes&lt;/id&gt;<tt> </tt> &lt;url&gt;http://cukes.info/maven&lt;/url&gt;<tt> </tt> &lt;/repository&gt;<tt> </tt> &lt;/repositories&gt;<tt> </tt> &lt;pluginRepositories&gt;<tt> </tt> &lt;pluginRepository&gt;<tt> </tt> &lt;id&gt;cukes&lt;/id&gt;<tt> </tt> &lt;url&gt;http://cukes.info/maven&lt;/url&gt;<tt> </tt> &lt;/pluginRepository&gt;<tt> </tt> &lt;/pluginRepositories&gt;<tt> </tt> &lt;dependencies&gt;<tt> </tt> &lt;dependency&gt;<tt> </tt> &lt;groupId&gt;cuke4duke&lt;/groupId&gt;<tt> </tt> &lt;artifactId&gt;cuke4duke&lt;/artifactId&gt;<tt> </tt> &lt;version&gt;${cuke4duke.version}&lt;/version&gt;<tt> </tt> &lt;scope&gt;test&lt;/scope&gt;<tt> </tt> &lt;/dependency&gt;<tt> </tt> &lt;dependency&gt;<tt> </tt> &lt;groupId&gt;org.picocontainer&lt;/groupId&gt;<tt> </tt> &lt;artifactId&gt;picocontainer&lt;/artifactId&gt;<tt> </tt> &lt;version&gt;2.11.2&lt;/version&gt;<tt> </tt> &lt;scope&gt;test&lt;/scope&gt;<tt> </tt> &lt;/dependency&gt;<tt> </tt> &lt;dependency&gt;<tt> </tt> &lt;groupId&gt;junit&lt;/groupId&gt;<tt> </tt> &lt;artifactId&gt;junit&lt;/artifactId&gt;<tt> </tt> &lt;version&gt;4.8.1&lt;/version&gt;<tt> </tt> &lt;scope&gt;test&lt;/scope&gt;<tt> </tt> &lt;/dependency&gt;<tt> </tt> &lt;/dependencies&gt;<tt> </tt> &lt;build&gt;<tt> </tt> &lt;plugins&gt;<tt> </tt> &lt;plugin&gt;<tt> </tt> &lt;groupId&gt;cuke4duke&lt;/groupId&gt;<tt> </tt> &lt;artifactId&gt;cuke4duke-maven-plugin&lt;/artifactId&gt;<tt> </tt> &lt;version&gt;${cuke4duke.version}&lt;/version&gt;<tt> </tt> &lt;configuration&gt;<tt> </tt> &lt;jvmArgs&gt;<tt> </tt> &lt;!-- Debugging. See http://wiki.github.com/aslakhellesoy/cuke4duke/debug-cuke4duke-steps --&gt;<tt> </tt> &lt;!--jvmArg&gt;-Xdebug&lt;/jvmArg&gt;<tt> </tt> &lt;jvmArg&gt;-Xnoagent&lt;/jvmArg&gt;<tt> </tt> &lt;jvmArg&gt;-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=4000&lt;/jvmArg&gt;<tt> </tt> &lt;jvmArg&gt;-Xmx384m&lt;/jvmArg--&gt;<tt> </tt> &lt;jvmArg&gt;-Dcuke4duke.objectFactory=cuke4duke.internal.jvmclass.PicoFactory&lt;/jvmArg&gt;<tt> </tt> &lt;jvmArg&gt;-Dfile.encoding=UTF-8&lt;/jvmArg&gt;<tt> </tt> &lt;/jvmArgs&gt;<tt> </tt> &lt;!-- You may not need all of these arguments in your own project. We have a lot here for testing purposes... --&gt;<tt> </tt> &lt;cucumberArgs&gt;<tt> </tt> &lt;cucumberArg&gt;--require ${basedir}/target/test-classes&lt;/cucumberArg&gt;<tt> </tt> &lt;/cucumberArgs&gt;<tt> </tt> &lt;gems&gt;<tt> </tt> &lt;gem&gt;install cuke4duke --version ${cuke4duke.version}&lt;/gem&gt;<tt> </tt> &lt;!-- Behind a proxy? --&gt;<tt> </tt> &lt;gem&gt;install cuke4duke --version ${cuke4duke.version} --http-proxy ${http.proxy}&lt;/gem&gt;<tt> </tt> &lt;/gems&gt;<tt> </tt> &lt;/configuration&gt;<tt> </tt> &lt;executions&gt;<tt> </tt> &lt;execution&gt;<tt> </tt> &lt;id&gt;run-features&lt;/id&gt;<tt> </tt> &lt;phase&gt;integration-test&lt;/phase&gt;<tt> </tt> &lt;goals&gt;<tt> </tt> &lt;goal&gt;cucumber&lt;/goal&gt;<tt> </tt> &lt;/goals&gt;<tt> </tt> &lt;/execution&gt;<tt> </tt> &lt;/executions&gt;<tt> </tt> &lt;/plugin&gt;<tt> </tt> &lt;/plugins&gt;<tt> </tt> &lt;/build&gt;<tt> </tt>&lt;/project&gt;<tt> </tt><tt> </tt></pre></td> </tr></table> <ol> <li>This is a minimal configuration for maven using Cucumber for intetgration tests. All you need to do is change the project group (groupId), identifier (artifactId) and name (name). Also check the section on proxy setup – Its enabled by default because I need it.</li> <li>Create a <code>features</code> folder under your project root. This is where we&#8217;ll add features shortly.<br /> From the project root folder (where the pom.xml is), run the following command:<br /> <code>mvn -Dcucumber.installGems=true integration-test</code></li> <li>This will download the maven plugins required to run the project, Cuke4Duke so we can write some features, JRuby to run Cucumber, and Cucumber and its dependencies. Depending on your connection speed and whether you&#8217;ve run Maven before, this could take a while.<br /> You should see something like:</li> </ol><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[INFO] -- cuke4duke-maven-plugin:0.4.3:cucumber (run-features) @ cuke4duke-java-example --<tt> </tt>[INFO] 0 scenarios<tt> </tt>[INFO] 0 steps<tt> </tt>[INFO] 0m0.000s<tt> </tt>[INFO] ---------------------------------------------------------------------</pre></td> </tr></table> <p>[<span class="caps">INFO</span>] <span class="caps">BUILD</span> <span class="caps">SUCCESS</span><br /> [<span class="caps">INFO</span>]</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><tt> </tt></pre></td> </tr></table> <h2>Adding Features</h2> <p>Create a <code>features/calculator.feature</code> containing the following:<br /> <span style="color:green;">Feature:</span> Simple Calculator example<br /> <span style="color:blue;">As</span> a big number cruncher user<br /> <span style="color:blue;">I want</span> to be able to perform arithmetic<br /> <span style="color:blue;">So that</span> I can make lots of money</p> <span style="color:green;">Scenario:</span> Add 0 <span style="color:green;">Given</span> a calculator <span style="color:green;">When</span> I add 1 and 0 <span style="color:green;">Then</span> the result should be 1 <p>Now lets try testing our feature:<br /> <code>mvn integration-test</code><br /> You should see something like:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt>18<tt> </tt>19<tt> </tt><strong>20</strong><tt> </tt>21<tt> </tt>22<tt> </tt>23<tt> </tt>24<tt> </tt>25<tt> </tt>26<tt> </tt>27<tt> </tt>28<tt> </tt>29<tt> </tt><strong>30</strong><tt> </tt>31<tt> </tt>32<tt> </tt>33<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[INFO] -- cuke4duke-maven-plugin:0.4.3:cucumber (run-features) @ cuke4duke-java-example --<tt> </tt>[INFO] Feature: Simple Calculator example<tt> </tt>[INFO] As a big number cruncher user<tt> </tt>[INFO] I want to be able to perform arithmetic<tt> </tt>[INFO] So that I can make lots of money<tt> </tt>[INFO] <tt> </tt>[INFO] Scenario: Add 0 # features/calculator.feature:6<tt> </tt>[INFO] Given a calculator # features/calculator.feature:7<tt> </tt>[INFO] When I add 1 and 0 # features/calculator.feature:8<tt> </tt>[INFO] Then the result should be 1 # features/calculator.feature:9<tt> </tt>[INFO] <tt> </tt>[INFO] 1 scenario (1 undefined)<tt> </tt>[INFO] 3 steps (3 undefined)<tt> </tt>[INFO] 0m0.024s<tt> </tt>[INFO] <tt> </tt>[INFO] You can implement step definitions for undefined steps with these snippets:<tt> </tt>[INFO] <tt> </tt>[INFO] Given /^a calculator$/ do<tt> </tt>[INFO] pending # express the regexp above with the code you wish you had<tt> </tt>[INFO] end<tt> </tt>[INFO] <tt> </tt>[INFO] When /^I add (\d+) and (\d+)$/ do |arg1, arg2|<tt> </tt>[INFO] pending # express the regexp above with the code you wish you had<tt> </tt>[INFO] end<tt> </tt>[INFO] <tt> </tt>[INFO] Then /^the result should be (\d+)$/ do |arg1|<tt> </tt>[INFO] pending # express the regexp above with the code you wish you had<tt> </tt>[INFO] end<tt> </tt>[INFO] <tt> </tt>[INFO] If you want snippets in a different programming language, just make sure a file<tt> </tt>[INFO] with the appropriate file extension exists where cucumber looks for step definitions.<tt> </tt>[INFO] <tt> </tt>[INFO] ---------------------------------------------------------------------</pre></td> </tr></table> <p>[<span class="caps">INFO</span>] <span class="caps">BUILD</span> <span class="caps">SUCCESS</span><br /> [<span class="caps">INFO</span>]</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"></pre></td> </tr></table> <p>What that&#8217;s saying is that you have 1 scenario that cannot be completed because it contains undefined steps, and that there are 3 undefined steps.<br /> It then gives you some examples on how to create the necessary step definitions. Unfortunately they are in Ruby, but as soon as we have the first java example, you&#8217;ll see that the suggested solutions are converted to Java.</p> <h2>Defining Steps</h2> <p>Create a src/test/java/cukes/CalculatorSteps.java (The package doesn&#8217;t matter):</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="kw">package</span> cukes;<tt> </tt><span class="kw">import</span> <span class="ic">cuke4duke.annotation.I18n.EN.Given</span>;<tt> </tt><span class="kw">import</span> <span class="ic">cuke4duke.annotation.I18n.EN.Then</span>;<tt> </tt><span class="kw">import</span> <span class="ic">cuke4duke.annotation.I18n.EN.When</span>;<tt> </tt><span class="kw">import</span> <span class="ic">cuke4duke.annotation.Pending</span>;<tt> </tt><span class="kw">import</span> <span class="ic">java.math</span>.*;<tt> </tt><span class="kw">import</span> <span class="ic">static</span> <span class="ic">org.junit.Assert.assertThat</span>;<tt> </tt><span class="kw">import</span> <span class="ic">static</span> <span class="ic">org.hamcrest.CoreMatchers</span>.*;<tt> </tt><span class="di">public</span> <span class="ty">class</span> <span class="cl">CalculatorSteps</span> {<tt> </tt> <span class="at">@Given</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^a calculator$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="at">@Pending</span><tt> </tt> <span class="di">public</span> <span class="ty">void</span> setUpCalculator() {<tt> </tt> }<tt> </tt>}<tt> </tt></pre></td> </tr></table> <p>Rerun the integration tests.</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt>18<tt> </tt>19<tt> </tt><strong>20</strong><tt> </tt>21<tt> </tt>22<tt> </tt>23<tt> </tt>24<tt> </tt>25<tt> </tt>26<tt> </tt>27<tt> </tt>28<tt> </tt>29<tt> </tt><strong>30</strong><tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[INFO] -- cuke4duke-maven-plugin:0.4.3:cucumber (run-features) @ cuke4duke-java-example --<tt> </tt>[INFO] Feature: Simple Calculator example<tt> </tt>[INFO] As a big number cruncher user<tt> </tt>[INFO] I want to be able to perform arithmatic<tt> </tt>[INFO] So that I can make lots of money<tt> </tt>[INFO] <tt> </tt>[INFO] Scenario: Add 0 # features/calculator.feature:6<tt> </tt>[INFO] Given a calculator # CalculatorSteps.setUpCalculator()<tt> </tt>[INFO] TODO (Cucumber::Pending)<tt> </tt>[INFO] features/calculator.feature:7:in `Given a calculator'<tt> </tt>[INFO] When I add 1 and 0 # features/calculator.feature:8<tt> </tt>[INFO] Then the result should be 1 # features/calculator.feature:9<tt> </tt>[INFO] <tt> </tt>[INFO] 1 scenario (1 pending)<tt> </tt>[INFO] 3 steps (2 undefined, 1 pending)<tt> </tt>[INFO] 0m0.070s<tt> </tt>[INFO] <tt> </tt>[INFO] You can implement step definitions for undefined steps with these snippets:<tt> </tt>[INFO] <tt> </tt>[INFO] @When(&quot;^I add 1 and 0$&quot;)<tt> </tt>[INFO] @Pending<tt> </tt>[INFO] public void iAdd1And0() {<tt> </tt>[INFO] }<tt> </tt>[INFO] <tt> </tt>[INFO] @Then(&quot;^the result should be 1$&quot;)<tt> </tt>[INFO] @Pending<tt> </tt>[INFO] public void theResultShouldBe1() {<tt> </tt>[INFO] }<tt> </tt>[INFO] <tt> </tt>[INFO] ---------------------------------------------------------------------</pre></td> </tr></table> <p>[<span class="caps">INFO</span>] <span class="caps">BUILD</span> <span class="caps">SUCCESS</span><br /> [<span class="caps">INFO</span>]</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"></pre></td> </tr></table> <p>You&#8217;ll see we now have 1 pending step i.e. there&#8217;s only a stub for the step for now. The @Pending annotation allows you to work on translating the steps into method calls first, and then worry about their implementation later (without forgetting to do them) Also, the examples are now Java because Cuke4Duke has figured out what language we&#8217;re using.<br /> Define the rest of the steps:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"> <span class="at">@When</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^I add 1 and 0$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="at">@Pending</span><tt> </tt> <span class="di">public</span> <span class="ty">void</span> addTwoNumbers() {<tt> </tt> }<tt> </tt> <span class="at">@Then</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^the result should be 1$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="at">@Pending</span><tt> </tt> <span class="di">public</span> <span class="ty">void</span> theResultShouldBe() {<tt> </tt> }<tt> </tt></pre></td> </tr></table> <p>Your features should pass with 1 scenario pending.<br /> Lets add some code to the test (Remember to take out the @Pending):</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"> Calculator calc;<tt> </tt> <span class="at">@Given</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^a calculator$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="di">public</span> <span class="ty">void</span> setUpCalculator() {<tt> </tt> calc = <span class="kw">new</span> Calculator();<tt> </tt> }<tt> </tt></pre></td> </tr></table> <p>Now we&#8217;re into familiar <span class="caps">TDD</span> territory. Start adding code until the tests pass<br /> And create our simple implementation class in <code>src/main/java/cukes:</code></p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt>18<tt> </tt>19<tt> </tt><strong>20</strong><tt> </tt>21<tt> </tt>22<tt> </tt>23<tt> </tt>24<tt> </tt>25<tt> </tt>26<tt> </tt>27<tt> </tt>28<tt> </tt>29<tt> </tt><strong>30</strong><tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="kw">package</span> cukes;<tt> </tt><span class="kw">import</span> <span class="ic">java.math</span>.*;<tt> </tt><tt> </tt><span class="di">public</span> <span class="ty">class</span> <span class="cl">Calculator</span> {<tt> </tt> <span class="di">public</span> <span class="pt">BigDecimal</span> add(<span class="pt">BigDecimal</span> value1, <span class="pt">BigDecimal</span> value2) {<tt> </tt> <span class="kw">return</span> value1.add(value2);<tt> </tt> }<tt> </tt>}<tt> </tt>Run the tests again <span class="er">â</span><span class="er">€</span><span class="er">“</span> now we<span class="s"><span class="dl">'</span><span class="k">ve got 1 passed, 1 pending &amp; 1 skipped step<tt> </tt>Finish off the rest of the test:<tt> </tt><tt> </tt> public class CalculatorSteps {<tt> </tt> Calculator calc;<tt> </tt> BigDecimal lastResult;<tt> </tt><tt> </tt> @Given(&quot;^a calculator$&quot;)<tt> </tt> public void setUpCalculator() {<tt> </tt> calc = new Calculator();<tt> </tt> }<tt> </tt><tt> </tt> @When(&quot;^I add 1 and 0$&quot;)<tt> </tt> public void addTwoNumbers() {<tt> </tt> lastResult = calc.add(new BigDecimal(1), new BigDecimal(0));<tt> </tt> }<tt> </tt><tt> </tt> @Then(&quot;^the result should be 1$&quot;)<tt> </tt> public void theResultShouldBe() {<tt> </tt> assertThat(lastResult, is(new BigDecimal(1)));<tt> </tt> }<tt> </tt>}<tt> </tt></span></span></pre></td> </tr></table> <p>I&#8217;m sure some of you are screaming about all these hard-coded values, and wondering what&#8217;s the point of defining everything twice, once in the feature, and again in the step class. Hang in there, we&#8217;ll tidy it up in a minute.<br /> Now lets add another scenario to our calculator.feature:</p> <span style="color:green;">Scenario:</span> Add 1 <span style="color:green;">Given</span> a calculator <span style="color:green;">When</span> I add 1 and 1 <span style="color:green;">Then</span> the result should be 2 <p>Running the tests tells us we need to add new steps. Instead of hard coding each scenario, lets <span class="caps">DRY</span>, and use RegEx to group out the variables on our existing step methods:<br /> Change the RegEx to something like:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">@When(&quot;^I add (\\d*) and (\\d*)$&quot;)<tt> </tt>@Then(&quot;^the result should be (\\d*)$&quot;)<tt> </tt></pre></td> </tr></table> <p>Run your tests again<br /> I got:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">[INFO] Scenario: Add 0 # features/calculator.feature:6<tt> </tt>[INFO] Given a calculator # CalculatorSteps.setUpCalculator()<tt> </tt>[INFO] When I add 1 and 0 # CalculatorSteps.addTwoNumbers()<tt> </tt>[INFO] java.lang.ArrayIndexOutOfBoundsException: 0 (NativeException)<tt> </tt>[INFO] features/calculator.feature:8:in `When I add 1 and 0'<tt> </tt>[INFO] Then the result should be 1 # CalculatorSteps.theResultShouldBe()<tt> </tt></pre></td> </tr></table> <p>This error message is a bit cryptic, and it confused me for a while until I remembered that the RegEx groups are converted into method arguments, so we need change our methods as follows:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"> <span class="at">@When</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^I add (</span><span class="ch">\\</span><span class="k">d*) and (</span><span class="ch">\\</span><span class="k">d*)$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="di">public</span> <span class="ty">void</span> addTwoNumbers(<span class="pt">BigDecimal</span> value1, <span class="pt">BigDecimal</span> value2) {<tt> </tt> lastResult = calc.add(value1, value2);<tt> </tt> }<tt> </tt><tt> </tt> <span class="at">@Then</span>(<span class="s"><span class="dl">&quot;</span><span class="k">^the result should be (</span><span class="ch">\\</span><span class="k">d*)$</span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="di">public</span> <span class="ty">void</span> theResultShouldBe(<span class="pt">BigDecimal</span> expectedResult) {<tt> </tt> assertThat(lastResult, is(expectedResult));<tt> </tt> }<tt> </tt></pre></td> </tr></table> <p>Cuke4Duke does automatic conversions from String to your argument type.<br /> Run the tests again, and you should see that you&#8217;ve now defined a testing language where the business analysts can add scenarios to their hearts content. Until something breaks, or the BA&#8217;s need more syntax, the developer&#8217;s job is done!</p> <p>That concludes our trivial introduction to Cucumber for Java. I hope that it&#8217;s shown just how expressive tests can (and should be). I also hope that next time you have develop something, you&#8217;ll have a go at expressing it as set of a Given/When/Then&#8217;s &#8211; even if you never actually write it down, let alone run it &#8211; just to see how it affects your thinking.</p> tag:codewright.biz,2008:Post/1 2011-08-16T19:15:50Z 2011-08-16T19:15:50Z The Realities of "The 24 Hour Work Week" <p>A while back I read a book by Tim Ferris called &#8220;The 24 Hour Work Week&#8221;. One of the major things he recommended was using online, cheap, international resources to do your work for you.</p> <p>I&#8217;ve finally taken the plunge, and recruited some graphic designers &#8211; they designed the logo, and website you see here. And all of this done through an incredible website called eLance (as in free-lance). I&#8217;ll tell you more about my experience with this site, and working with international (read Pakistani in this case) teams shortly</p>