<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;AkUGQ30yeip7ImA9WhRaEEo.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511</id><updated>2012-02-13T09:23:42.392+13:00</updated><category term="Hudson" /><category term="SchemaSpy" /><category term="FEST" /><category term="Crap4J" /><category term="CITCON" /><category term="Code Quality" /><category term="Assert Given Clause" /><category term="Given-When-Then" /><category term="Concordion" /><category term="Groovy" /><category term="Test Failure" /><category term="WebDriver" /><title>Tutan's blog</title><subtitle type="html">Thoughts on Java, Groovy and Agile Practices</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://tutansblog.blogspot.com/" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/tutansblog" /><feedburner:info uri="tutansblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>tutansblog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;D0QBRX48cSp7ImA9WhRRGUQ.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-63451641362987951</id><published>2011-12-04T10:00:00.010+13:00</published><updated>2011-12-04T23:42:34.079+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-04T23:42:34.079+13:00</app:edited><title>YOW! Retrospective</title><content type="html">The YOW! 2011 conference in Melbourne was fantastic. My brain is totally buzzing from the great presentations, discussions and energy from the conference.  Before the buzz settles, here's my personal retrospective.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;YOW will be making the presentations and videos available online in due course.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;1 - What I Learnt&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;Key themes&lt;/span&gt;&lt;/b&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Continuous Delivery of Business Value&lt;/li&gt;&lt;li&gt;JS everywhere, REST, push technologies, HTML5&lt;/li&gt;&lt;li&gt;Having fun with technology&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;meta equiv="content-type" content="text/html; charset=utf-8"&gt;&lt;b&gt;Continuous Delivery/Business Value sessions&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Continuous Design - Mary Poppendieck&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Design Thinking:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Assemble Diverse Team&lt;/li&gt;&lt;li&gt;Frame the problem&lt;/li&gt;&lt;li&gt;Ideation - prototype&lt;/li&gt;&lt;li&gt;Experimentation&lt;/li&gt;&lt;li&gt;Iterate back to 3, or Reformat the problem/pivot back to 2&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Continuous Design puts coding into this loop&lt;/div&gt;&lt;/div&gt;&lt;div&gt;   &lt;/div&gt;&lt;div&gt;The Learning Cycle. Learn -&amp;gt; Model -&amp;gt; Build -&amp;gt; Measure -&amp;gt; Learn ...&lt;/div&gt;&lt;div&gt;The fastest learner wins.&lt;/div&gt;&lt;div&gt;A small, experienced, dedicated, self empowered team can introduce 4x more product - George Stalk, competing against time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Need good people, whole team (marketing, design, dev, test, ops, support)&lt;/div&gt;&lt;div&gt;Measure product metrics not project metrics&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Amazon.com, working backwards:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Write a press release &lt;/li&gt;&lt;li&gt;Write FAQs&lt;/li&gt;&lt;li&gt;Describe customer experience&lt;/li&gt;&lt;li&gt;(Write User Manual)&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;Innovation Accounting&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Start with hypothesis - a business growth model&lt;/li&gt;&lt;li&gt;Establish a baseline - the Minimum Viable Product&lt;/li&gt;&lt;li&gt;Target every initiative at a Growth Metric&lt;/li&gt;&lt;li&gt;Split Test A/B&lt;/li&gt;&lt;li&gt;Definition of Done includes feature value validated&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Cohort Metrics&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Measure a cohort of new users every week&lt;/li&gt;&lt;li&gt;Track behaviour of each cohort against product events and changes&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Mary then covered some continuous delivery principles - which made Jez Humble and Martin Fowler adjust their subsequent presentation somewhat :-)&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Low Dependency Architecture&lt;/li&gt;&lt;li&gt;Feature Toggles&lt;/li&gt;&lt;li&gt;Trunk-based development&lt;/li&gt;&lt;li&gt;Branch by abstraction&lt;/li&gt;&lt;li&gt;Canary releasing&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Continuous Delivery - Jez Humble, Martin Fowler&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;This covered many of the concepts from the excellent &lt;a href="http://www.amazon.com/Continuous-Delivery-Deployment-Automation-Addison-Wesley/dp/0321601912"&gt;Continuous Delivery&lt;/a&gt; book. I won't repeat this content - go read the book.  Some useful soundbites from the talk:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;On acquisition, Flickr were initially told by Yahoo to stop continuous deployment. The Flickr team then gained stats that they had less downtime than any of the other Yahoo services.  Flickr deploy approx 10x/day to production. Downtime was 4 x 6mins per year.&lt;/li&gt;&lt;li&gt;Mingle has 20 hours of acceptance tests.  Running in parallel across 70 boxes, these complete in 45 minutes.&lt;/li&gt;&lt;li&gt;On any acceptance test failure you should find out how it got through, and beef up your commit tests to catch the failure earlier.&lt;/li&gt;&lt;li&gt;The job of the deployment pipeline is to kill code that isn't suitable for release&lt;/li&gt;&lt;li&gt;Devops - development is measured on throughput, operations on stability.  Need to establish a culture of collaboration, automation, measurement, sharing.&lt;/li&gt;&lt;li&gt;Dark launching - deploy back end service first before switching it on&lt;/li&gt;&lt;li&gt;Benefits in decoupling deployment and release (eg. using feature toggles). Facebook has every major feature to be released in the next 6 months already deployed to production, and waiting to be released.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;i&gt;Introducing Continuous Delivery&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Focus on behaviours, not tools&lt;/li&gt;&lt;li&gt;People are the key&lt;/li&gt;&lt;li&gt;Get everyone together at the beginning&lt;/li&gt;&lt;li&gt;Keep meeting&lt;/li&gt;&lt;li&gt;Make it easy for everyone to see what's happening (eg. big visible display)&lt;/li&gt;&lt;li&gt;Kaizen. Continuous Delivery should be part of continuous improvement process. It's going to take months. Do it step-by-step.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;ITIL and Continuous Delivery&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Jez provided an excellent answer to a question about using Continuous Delivery with ITIL. See the last 5 minutes of the video (when it comes out).&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;The deployment pipeline is a superior way to manage risk (over paperwork)&lt;/li&gt;&lt;li&gt;Under ITIL v3, continuous delivery can be approved as a standard change with known low risk.&lt;/li&gt;&lt;li&gt;2 other strategies for integrating with ITIL v3&lt;/li&gt;&lt;li&gt;Need to work closely with auditor&lt;/li&gt;&lt;li&gt;Auditors love deployment pipelines, everything you could possibly audit is available and version controlled.  &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;b&gt;Adventures in Infrastructure as code - Julian Simpson&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Julian demonstrated &lt;a href="https://github.com/builddoctor/infrastructure-as-code-examples"&gt;code&lt;/a&gt; using Puppet and Chef, that installs Ubuntu, nginx, jetty and deploys a war file.  In short, Puppet is configuration-based and appeals more to sysadmins who like to see all the moving parts. Chef is convention-based and tends to appeal to developers more.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;He discussed testing using cucumber-puppet and cucumber-nagios, and mentioned cfengine, vagrant and the veewee plugin.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;b&gt;The Limited Red Society - Joshua Kerievsky&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Parallel to the Limited WIP Society, the goal is to minimise the development time spent in the red (failing tests), or in the pink (code that won't compile).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;By visualising the development flow, developers get feedback for improvement.  Analogies with sports visualisations.  Joshua recommended "&lt;a href="http://www.amazon.com/Moneyball-Art-Winning-Unfair-Game/dp/0393324818/"&gt;The art of winning an unfair game&lt;/a&gt;" book, on which the Moneyball movie was based.&lt;/div&gt;&lt;div&gt;&lt;u&gt;&lt;span class="Apple-style-span" style="-webkit-text-decorations-in-effect: none; "&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/span&gt;&lt;/u&gt;&lt;/div&gt;&lt;div&gt;Measure and practice the Shortest Longest Red Etude. A good retreat is better than a bad stand.&lt;/div&gt;&lt;div&gt;&lt;u&gt;&lt;span class="Apple-style-span" style="-webkit-text-decorations-in-effect: none; "&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/span&gt;&lt;/u&gt;&lt;/div&gt;&lt;div&gt;Refactoring strategies missed in &lt;a href="http://www.amazon.com/Refactoring-Patterns-Joshua-Kerievsky/dp/0321213351"&gt;Joshua's book&lt;/a&gt;:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Parallel Change - write refactored code alongside current code and gradually switch over (like building a 2nd bridge)&lt;/li&gt;&lt;li&gt;Narrowed Change - reduce the number of change points (eg. using Extract Method), before making the change.  This is preferred over parallel change.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;We had covered these in depth on Joshua's excellent Refactoring Workshop on Wednesday. The eLearning package that Industrial Logic have put together is exceptional, with IDE plugins to allow us to visualise our refactoring sessions and provide feedback on our work. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The development visualisation is available in Industrial Logic's &lt;a href="https://elearning.industriallogic.com/gh/submit?Action=AlbumContentsAction&amp;amp;album=sessions&amp;amp;devLanguage=Java"&gt;Sessions&lt;/a&gt; tool.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;Technology Sessions&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Domain-driven design for RESTful Systems - Jim Webber&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;HTTP is an Application Protocol. You have to narrow it down to your Domain Application Protocol.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PUT is idempotent. Always use it rather than POST for financial transactions. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Jim presented some of the Restbucks examples from his book.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;His talk was very engaging.  It was somewhat alarming to find that only 1 of the audience had played a PS/1 or later as their first computer game, while over 50% of the audience had first played games that were loaded from cassette tape. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;The Live Web - Drag n' drop in the Cloud - Dan Ingalls&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;A component architecture for HTML5. Builds a structured scene graph in JavaScript. Integrates HTML, SVG and Canvas graphics. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's like Squeak (and Scratch, BYOB, Snap) but runs behind firewall&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Kit approach - components, connections, encapsulation&lt;/div&gt;&lt;div&gt;Parts bin - cloud based repository of active content&lt;/div&gt;&lt;div&gt;Live connect, dataflow binding&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The IDE runs using Lively Web, and saves using WebDAV (to node.js, to svn)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Not sure how I'd use this for commerical projects yet, but it's a lot of fun and I'll be trying it out with my son.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Better testing with less work - John Hughes&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I've previously looked at (and discounted) QuickCheck in Java, but the Erlang version does so much more that I'll be looking at it again.  There is a lot of difference between the different language implementations.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;QuickCheck allows us to write general properties that a system should meet.  The properties should hold true across a range of values (similar to JUnit Theories).    It then generates random inputs to test those properties.  On failure it will retry with simpler test cases until it finds the minimal test case that fails.  This simplifies debugging.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A particular sweet spot for QuickCheck is finding race conditions.  It's easy to convert sequential to parallel tests.  John used QuickCheck to easily find some race conditions in a database implementation that had been eluding the developers for months.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;NodeJS and the JavaScript-everywhere strategy - Matthew Eernisse&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Matthew discussed how they're using Node at Yammer.  Three of their customer-facing services use Node - the Upload service, the collaborative document editing service and an undisclosed new service. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Internally, Yammer use Jake and FooUnit for build and test.  It's good to see Yammer developers open sourcing some of their work on Github. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Matthew described the Geddy web framework and showed how they use the yammer.util.promise framework for async code. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Webbit - a lightweight WebSocket web server for Java - Aslak Hellesoy&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Webbit is a single-threaded, non-blocking, Java-based, high throughput web server (no servlet API).  It supports 3 models:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;HTTP request-response&lt;/li&gt;&lt;li&gt;WebSocket (long-lived 2 way connections)&lt;/li&gt;&lt;li&gt;EventSource (long-lived push server-&amp;gt;client connections)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;EventSource is a W3C standard and is implemented in most decent browsers. It's more lightweight than WebSockets.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At DRW, Aslak's team ported a trading app to Webbit, Chrome, WebSockets.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aslak coded a HTTP client app in less than 100 lines (including Java, JS, HTML, CSS). See &lt;a href="https://gist.github.com/1421652"&gt;https://gist.github.com/1421652&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Blocking or long operations must be offloaded to another thread.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Webbit has no hot-deploy capability out-of-the-box, and does not support HTTPS.  (Aslak recommended &lt;a href="https://github.com/bumptech/stud"&gt;stud&lt;/a&gt; as a HTTPS terminating proxy).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For testing, Webbit provides StubRequest, StubResponse and StubConnection classes.  For end-to-end tests, the server can be started and stopped in 1.5ms.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Webbit tends to be embedded in an app (rather than the app being embedded in the web server). It has a small footprint, using about 100kb RAM and almost no CPU when idle. It has REST (JAX/RS) and RPC add-ons.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Above the Clouds - Introducing Akka - Peter Vlugter&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It was a surprise to find out that NZ has a developer working for Typesafe as part of the core Akka development team. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Peter introduced the core features - Actors, STM (Software Transactional Memory) and Dataflow (a way of composing futures, inspired by the Oz framework).  They are looking at supporting Agents and Transactors (2 phase commit STM).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Akka supports Scala and Java APIs.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Peter gave a good, detailed introduction to Scala Actors. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Akka integrations include Camel, Spring, ZeroMQ, AMQP, Play framework, REST. It has a testing toolkit (and Peter is keen to explore a QuickCheck for actors),  It is open-source, except for the commercial Atmos tool (trace, monitor, manage, provision).&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;General&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The keynotes were entertaining and insightful.  Hearing Cameron Prudy describe potential JVM enhancements gave me some warm and fuzzies about Java's future as a platform.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Mike Lee, Damian Conway, Mary Poppendieck, Simon Peyton-Jones and Jim Webber were outstanding speakers.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Thanks to the great company - especially John Hurst, Martin Paulo, Paul and Luke (the AEMOs) and Balaji, and to everyone else that made this great.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And above all, thanks to Dave Thomas, the YOW committee, the volunteers and speakers who made YOW happen.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;WOW - that turned into an epic.  Illustrating just how much I got out of YOW!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;2 - What I&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt; will&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt; do next&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Books to read&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/Lean-Startup-Entrepreneurs-Continuous-Innovation/dp/0307887898"&gt;The Lean Startup&lt;/a&gt; - Eric Ries&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.amazon.com/REST-Practice-Hypermedia-Systems-Architecture/dp/0596805829"&gt;REST in Practice&lt;/a&gt; - Jim Webber&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Technologies to play with&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Lively Web&lt;/div&gt;&lt;div&gt;QuickCheck&lt;/div&gt;&lt;div&gt;Node.js&lt;/div&gt;&lt;div&gt;Webbit&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Presentations to watch online (that I missed)&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The Post-Java Virtual Machine - Ola Bini&lt;/div&gt;&lt;div&gt;JRuby for the win - Ola Bini&lt;/div&gt;&lt;div&gt;Three 'Tall' Tales - Kevin O'Neill (mobile dev and testing)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;3 - Impediments&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Above all, the speakers brought out the joy of programming (especially Damian Conway!)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My main impediment is time.  I really need to re-instate my "20% time" for research, play, innovation and general hacking.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Must be about time for my new year's resolutions..&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-63451641362987951?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/ucwYpYYzaH4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/63451641362987951/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=63451641362987951" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/63451641362987951?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/63451641362987951?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/ucwYpYYzaH4/yow-retrospective.html" title="YOW! Retrospective" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2011/12/yow-retrospective.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkAAQH84eCp7ImA9WhdaE0k.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-2516900356779410380</id><published>2011-10-23T17:52:00.009+13:00</published><updated>2011-10-23T18:45:41.130+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-23T18:45:41.130+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="Test Failure" /><category scheme="http://www.blogger.com/atom/ns#" term="Given-When-Then" /><category scheme="http://www.blogger.com/atom/ns#" term="Assert Given Clause" /><title>A message from the past from someone that cared</title><content type="html">Last year I saw a tweet that a failing unit test was "a message from the past from someone that cared".&lt;br /&gt;&lt;br /&gt;I was reminded of this recently when picking up a project that hadn't been touched for over 2 years. One of the acceptance tests failed with the following error:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-7Q6yuxGb0ZA/TqOm3WB6_eI/AAAAAAAAAtw/ZbXNonmOLhY/s1600/chbhdijg.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 600px;" src="http://1.bp.blogspot.com/-7Q6yuxGb0ZA/TqOm3WB6_eI/AAAAAAAAAtw/ZbXNonmOLhY/s400/chbhdijg.png" alt="" id="BLOGGER_PHOTO_ID_5666556225898872290" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The error message was sufficiently detailed to lead me straight to the solution. Thankfully the test writer&lt;sup&gt;1&lt;/sup&gt; had cared enough to make sure that the failing test provided sufficient detail to be easily fixed.&lt;br /&gt;&lt;br /&gt;The original tweeter was no doubt referring to failing unit tests helping to detect unwanted code regressions.  When relying on external configuration or data, tests should also check any assumptions they are making about the data.  In this case, the Given clause of the "Given-When-Then" statement is clear about its expectations, and the test fixture is checking that these preconditions are met.&lt;br /&gt;&lt;br /&gt;This type of environment issue could even be treated as a different type of test failure, as discussed in &lt;a href="http://tutansblog.blogspot.com/2010/06/triaging-test-failures.html"&gt;Triaging Test Failures&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;1&lt;/sup&gt; OK the test writer was me :-)  But since I hadn't worked on the application for over 2 years, it may as well have been someone else!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-2516900356779410380?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/oXuToRTTJrQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/2516900356779410380/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=2516900356779410380" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/2516900356779410380?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/2516900356779410380?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/oXuToRTTJrQ/message-from-past-from-someone-that.html" title="A message from the past from someone that cared" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-7Q6yuxGb0ZA/TqOm3WB6_eI/AAAAAAAAAtw/ZbXNonmOLhY/s72-c/chbhdijg.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2011/10/message-from-past-from-someone-that.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYCQHg8eSp7ImA9Wx9bF0k.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-3062963243175145704</id><published>2011-02-26T23:16:00.002+13:00</published><updated>2011-02-27T07:16:01.671+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-27T07:16:01.671+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Groovy" /><title>Getting Groovy with Jar Versions</title><content type="html">&lt;p&gt;Oh how I love the Groovy!&lt;/p&gt;&lt;p&gt;I just needed to check version numbers of jar files in multiple folders and collate a table like:&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-QP5XxzuDA94/TWjTliTVitI/AAAAAAAAAa0/9EatAj1YpYc/s1600/versions.gif"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 373px;" src="http://2.bp.blogspot.com/-QP5XxzuDA94/TWjTliTVitI/AAAAAAAAAa0/9EatAj1YpYc/s400/versions.gif" alt="" id="BLOGGER_PHOTO_ID_5577940780314888914" border="0" /&gt;&lt;/a&gt;While most jar files have the version number in either the Bundle-Version or Implementation-Version attributes of the MANIFEST.MF file, some do not follow this convention, and we need to either look for a file ending in _VERSION, or parse a pom.xml within a META-INF subfolder to get the version number.&lt;/p&gt;&lt;p&gt;Groovy makes it so easy to do this stuff.  On top of this, creating the HTML table is a breeze.  The icing on the cake is to save the file and open in Firefox in a couple of lines.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/845093.js?file=PublishJarVersions.groovy"&gt;&lt;/script&gt;&lt;br /&gt;&lt;p&gt;This would have taken hundreds of lines in Java.  Groovy makes it concise, while allowing access to all Java libraries, resulting in code that is readable (if a little alien at first) to a Java developer.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-3062963243175145704?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/7YgwHOtVx1c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/3062963243175145704/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=3062963243175145704" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3062963243175145704?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3062963243175145704?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/7YgwHOtVx1c/getting-groovy-with-jar-versions.html" title="Getting Groovy with Jar Versions" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-QP5XxzuDA94/TWjTliTVitI/AAAAAAAAAa0/9EatAj1YpYc/s72-c/versions.gif" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2011/02/getting-groovy-with-jar-versions.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAMRnsyeyp7ImA9Wx9XEE0.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-7892065884491202577</id><published>2011-01-03T10:09:00.003+13:00</published><updated>2011-01-03T10:13:07.593+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-03T10:13:07.593+13:00</app:edited><title>concordion-extensions is now a Concordion sub-project</title><content type="html">I'm pleased to announce that concordion-extensions is now a sub-project  of Concordion.  This project includes extensions to add &lt;a href="http://tutansblog.blogspot.com/2010/10/displaying-screenshots-on-acceptance.html"&gt;screenshots&lt;/a&gt;  and &lt;a href="http://tutansblog.blogspot.com/2010/09/whats-happening-in-my-acceptance-tests.html"&gt;logging information&lt;/a&gt; to Concordion output, and to change the format of  the timestamp in the footer.  See &lt;a class="moz-txt-link-freetext" href="http://concordion.org/Extensions.html"&gt;http://concordion.org/Extensions.html&lt;/a&gt;  for further details.&lt;br /&gt;&lt;br /&gt;An updated release, concordion-extensions v1.0.1, is now also available  in Maven central.  See &lt;a class="moz-txt-link-freetext" href="http://concordion.org/Download.html"&gt;http://concordion.org/Download.html&lt;/a&gt; for details.&lt;br /&gt;&lt;br /&gt;The classes have been repackaged under org.concordion.ext.  Any users of  concordion-extensions v1.0.0 will need to update their package  definitions with this new release.&lt;br /&gt;&lt;br /&gt;If anyone has ideas for additional extensions, please add them to  &lt;a class="moz-txt-link-freetext" href="http://code.google.com/p/concordion-extensions/wiki/ExtensionIdeas"&gt;http://code.google.com/p/concordion-extensions/wiki/ExtensionIdeas&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-7892065884491202577?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/dsY1NolFQ74" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/7892065884491202577/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=7892065884491202577" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/7892065884491202577?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/7892065884491202577?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/dsY1NolFQ74/concordion-extensions-is-now-concordion.html" title="concordion-extensions is now a Concordion sub-project" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2011/01/concordion-extensions-is-now-concordion.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUEQ3g6fCp7ImA9Wx9XEE0.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-3902591394789900621</id><published>2010-10-16T22:37:00.021+13:00</published><updated>2011-01-03T10:03:22.614+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-03T10:03:22.614+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="WebDriver" /><category scheme="http://www.blogger.com/atom/ns#" term="Concordion" /><title>Displaying screenshots on acceptance test failures</title><content type="html">When running GUI tests it can be difficult to determine what was being shown on the GUI at the point of failure, especially if the tests are running minimised or on a CI server.&lt;br /&gt;&lt;br /&gt;We developed a screenshot extension for Concordion to include screenshots in the output whenever failures occur.  We've been finding this extension incredibly useful for diagnosing intermittent failures in our automated acceptance tests.&lt;br /&gt;&lt;br /&gt;This requires the new &lt;a href="http://www.concordion.org/"&gt;Concordion&lt;/a&gt; 1.4.1, which introduces support for extensions.  Amongst other things, extensions can add new listeners to commands and make changes to the Concordion output.&lt;br /&gt;&lt;br /&gt;The following example is running an end-to-end test using &lt;a href="http://code.google.com/p/selenium/?redir=1"&gt;WebDriver&lt;/a&gt; (Selenium 2) to test a Google search through the Firefox browser.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_EpHkfjCuniM/TLl7HnO4NLI/AAAAAAAAAZY/tKpT6J4wq84/s1600/allwhites.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 110px;" src="http://2.bp.blogspot.com/_EpHkfjCuniM/TLl7HnO4NLI/AAAAAAAAAZY/tKpT6J4wq84/s400/allwhites.png" alt="" id="BLOGGER_PHOTO_ID_5528585388294681778" border="0" /&gt;&lt;/a&gt;While the results show that the test is failing since Netherlands is displayed in the Google results, it would be helpful to see the actual results page.  The screenshot extension allows us to hover over the failure to see an image of the browser page at the time the failure occurred.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_EpHkfjCuniM/TMNV9u4zqLI/AAAAAAAAAaA/9-t6wrB7wt0/s1600/allwhites1.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 378px;" src="http://3.bp.blogspot.com/_EpHkfjCuniM/TMNV9u4zqLI/AAAAAAAAAaA/9-t6wrB7wt0/s400/allwhites1.png" alt="" id="BLOGGER_PHOTO_ID_5531359286387779762" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Clicking on the image opens it for further inspection:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_EpHkfjCuniM/TLl9Atp_VnI/AAAAAAAAAZo/Xa_PFgDv6CA/s1600/allwhites-google2.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 218px;" src="http://1.bp.blogspot.com/_EpHkfjCuniM/TLl9Atp_VnI/AAAAAAAAAZo/Xa_PFgDv6CA/s400/allwhites-google2.png" alt="" id="BLOGGER_PHOTO_ID_5528587468783179378" border="0" /&gt;&lt;/a&gt;In this example, we've configured the extension to use WebDriver's &lt;code&gt;TakesScreenshot&lt;/code&gt; interface, so we see an image of just the browser page, irrespective of whether it is currently visible on the screen.&lt;br /&gt;&lt;br /&gt;The extension can also be used to take screenshots on success, and includes a command to explicitly take screenshots.&lt;br /&gt;&lt;br /&gt;It is available in the &lt;a href="http://code.google.com/p/concordion-extensions"&gt;concordion-extensions&lt;/a&gt; project, along with the &lt;a href="http://tutansblog.blogspot.com/2010/09/whats-happening-in-my-acceptance-tests.html"&gt;Logging Tooltip Extension&lt;/a&gt;.  You'll need to set the &lt;code&gt;concordion.extensions&lt;/code&gt; system property to use it - see the &lt;a href="http://concordion.org/Extensions.html#library"&gt;documentation&lt;/a&gt; for details.&lt;br /&gt;&lt;br /&gt;The source code for this example is available in the &lt;a href="http://code.google.com/p/concordion-extensions/downloads/list"&gt;concordion-extensions-demo&lt;/a&gt; zip package.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Acknowledgements:&lt;/span&gt;&lt;br /&gt;This extension was partly inspired by Mark Derricutt's &lt;a href="http://github.com/talios/concordion-examples/blob/master/src/test/java/com/talios/ScreenshotCommand.java"&gt;ScreenshotCommand&lt;/a&gt;, and by Adam Setch's &lt;a href="http://tech.groups.yahoo.com/group/concordion/message/618"&gt;post&lt;/a&gt; to the Concordion list.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;UPDATES&lt;br /&gt;&lt;/span&gt;Oct 25 2010. Added acknowledgements&lt;br /&gt;Jan 03 2011. Project moved to Google Code and docs to Concordion site.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-3902591394789900621?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/mr9fNLdPGsU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/3902591394789900621/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=3902591394789900621" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3902591394789900621?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3902591394789900621?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/mr9fNLdPGsU/displaying-screenshots-on-acceptance.html" title="Displaying screenshots on acceptance test failures" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_EpHkfjCuniM/TLl7HnO4NLI/AAAAAAAAAZY/tKpT6J4wq84/s72-c/allwhites.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2010/10/displaying-screenshots-on-acceptance.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIAQX8ycCp7ImA9Wx9XEE0.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-326437117286325799</id><published>2010-09-12T21:32:00.014+12:00</published><updated>2011-01-03T10:09:00.198+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-03T10:09:00.198+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="Concordion" /><title>What's happening in my acceptance tests?</title><content type="html">Agile Acceptance Testing allows us to describe desired behaviour using examples that describe the business intent. &lt;a href="http://gojko.net/2010/06/16/anatomy-of-a-good-acceptance-test/"&gt; Good acceptance tests&lt;/a&gt; are written as plain language specifications, not scripts.  Implementation details are coded in a separate test "fixture" class.&lt;br /&gt;&lt;br /&gt;One downside of this approach is a loss of transparency of what the tests are actually doing.  The "fixtures" are often written by developers, which may need a leap in faith for testers to trust. On a recent project, this trust was dented when the tests didn't do what they were supposed to be doing.&lt;br /&gt;&lt;br /&gt;With this in mind, I set out to provide more insight into what our tests are actually doing, without undermining the principles of acceptance testing.&lt;br /&gt;&lt;br /&gt;The result is a logging extension for &lt;a href="http://concordion.org/"&gt;Concordion&lt;/a&gt;.  This adds a "tooltip" to the Concordion output HTML that shows log output when hovered over:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_EpHkfjCuniM/TI3lpp_SN_I/AAAAAAAAAZQ/Om1sxPvaf4M/s1600/concordion-annotate.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 194px;" src="http://4.bp.blogspot.com/_EpHkfjCuniM/TI3lpp_SN_I/AAAAAAAAAZQ/Om1sxPvaf4M/s400/concordion-annotate.jpg" alt="" id="BLOGGER_PHOTO_ID_5516317622407411698" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This tooltip is proving useful not only to testers, but also for developers to gain insight into what is happening in their tests and to find performance improvements.  For example, in the above example we were surprised to see the web page being loaded twice, and a number of element lookups being duplicated.&lt;br /&gt;&lt;br /&gt;This approach could also be used for user documentation of the steps required to complete an action, potentially with screen shots, or even as an embedded screen cast.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Implementation Details&lt;/span&gt;&lt;br /&gt;We've added a new extension mechanism to Concordion 1.4.1 to make it easy to add features such as this.&lt;br /&gt;&lt;br /&gt;This extension is available in the &lt;a href="http://code.google.com/p/concordion-extensions/"&gt;concordion-extensions&lt;/a&gt; project.   The extension captures java.util.logging output and has a number of configuration options.  You'll need to set the &lt;code&gt;concordion.extensions&lt;/code&gt; system property to use it - see the &lt;a href="http://concordion.org/Extensions.html#library"&gt;&lt;span style="text-decoration: underline;"&gt;documentation&lt;/span&gt;&lt;/a&gt;&lt;a href="http://github.com/nigelcharman/concordion-extensions"&gt;&lt;/a&gt; for details.&lt;br /&gt;&lt;br /&gt;For a example using this Concordion extension with WebDriver,  download the http://code.google.com/p/concordion-extensions/downloads/list package.&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;UPDATES&lt;br /&gt;&lt;/span&gt;Oct 4  2010. Changed github link to new concordion-extensions project&lt;br /&gt;Oct 6  2010. Source code moved to trunk of Concordion&lt;br /&gt;Oct 24 2010. Updated to reference Concordion 1.4.1 and concordion-extension-demo project&lt;br /&gt;Jan 03 2011. Project moved to Google Code and docs to Concordion site.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-326437117286325799?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/YMh1HQ4rSJk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/326437117286325799/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=326437117286325799" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/326437117286325799?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/326437117286325799?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/YMh1HQ4rSJk/whats-happening-in-my-acceptance-tests.html" title="What's happening in my acceptance tests?" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_EpHkfjCuniM/TI3lpp_SN_I/AAAAAAAAAZQ/Om1sxPvaf4M/s72-c/concordion-annotate.jpg" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2010/09/whats-happening-in-my-acceptance-tests.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIDRno6eCp7ImA9Wx5XE0o.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-586246822075472145</id><published>2010-06-27T09:57:00.014+12:00</published><updated>2010-09-13T23:32:57.410+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-13T23:32:57.410+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="CITCON" /><title>Triaging test failures</title><content type="html">One of the goals of Open Spaces conferences is to turn "corridor conversations" into the focal point of the conference.  This was aptly demonstrated at &lt;a href="http://www.citconf.com/"&gt;CITCON&lt;/a&gt; ANZ when Richard Vowles introduced a topic we'd been discussing over kebabs the night before.&lt;br /&gt;&lt;br /&gt;Richard has subsequently discussed the topic on the &lt;a href="http://www.illegalargument.com/"&gt;Illegal Argument&lt;/a&gt; podcast.  This post is an extension of the discussion.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The problem&lt;/span&gt;&lt;br /&gt;When running integration and acceptance tests, test failures may be caused by factors other than incorrect code.  This is most apparent when performing end-to-end testing through to Enterprise Information Systems. A number of factors can cause the test to fail - system unavailable, test data not in required state.&lt;br /&gt;&lt;br /&gt;It would be useful to categorise the failures by cause, for notification and reporting purposes. Developers should be notified of code-related issues, testers might be responsible for data issues, and sys ops for server errors.  Over time it would also be useful to visualise how often server and data errors are occurring.&lt;br /&gt;&lt;br /&gt;Richard provided the example:&lt;br /&gt;&lt;blockquote style="font-family: courier new; color: rgb(0, 0, 153);"&gt;Given a customer has been in arrears for over 90 days...&lt;/blockquote&gt;In order to run this test in an end-to-end environment, the test code has to get a customer in this state.  Richard's system uses an AS/400 back-end, and it simply is not possible to automate the setup of a customer in this state. The test code may need to be configured with a specific customer id, or it may be smart enough to search for a customer in the required state.&lt;br /&gt;&lt;br /&gt;Over time, the customer data may no longer be available.  For example, periodic data refreshes may remove or update the customer details.&lt;br /&gt;&lt;br /&gt;Richard's not the only one with this problem - I'm also seeing it on a current project.&lt;br /&gt;&lt;br /&gt;The problem of finding adequate test data is exacerbated when the test updates the state:&lt;br /&gt;&lt;blockquote style="font-family: courier new; color: rgb(0, 0, 153);"&gt;Given a customer has been in arrears for over 90 days,&lt;br /&gt;when her invoices are paid in full,&lt;br /&gt;then her status is changed to black.&lt;br /&gt;&lt;/blockquote&gt;&lt;span&gt;In this case, the destructive change to the customer's state means that the data is no longer suitable for running this test.  The test needs to find a different customer in arrears the next time it is run.  Since debtors are a finite resource, the test may be unrunnable at some stage.&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Why run these fragile end-to-end tests?&lt;br /&gt;&lt;/span&gt;&lt;span&gt;With &lt;/span&gt;&lt;span&gt;Agile Acceptance Testing &lt;/span&gt;&lt;span&gt;(ATDD, BDD etc), the focus is on testing business examples that will prove to the customer that a feature is "done".  Running the tests end-to-end provides the greatest assurance to the customer that the functional requirements are being met, and reduces the need for manual regression testing.&lt;br /&gt;&lt;br /&gt;Depending on the project, it may be possible to implement these tests "under the covers" of the user interface, or using mocks for back-end functionality.  We often use these approaches to drive the design of our code, possibly before the user interface or back-end are available.  However, these approaches don't provide the full benefits that we get from end-to-end tests.&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;"Unrunnable" test result&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;In the past, I have tackled this issue by making assertions on the Given clause of the tests.  If the test pre-conditions are not met, the test results in a failure.&lt;br /&gt;&lt;br /&gt;The proposal made at CITCON is to introduce a new "Unrunnable" test result state.  This state is neither success nor failure.  The discussion led to introducing a new colour  for this state, to differentiate it from red (failure) and green (success).&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Triaging test results&lt;/span&gt;&lt;br /&gt;Extending the idea, it would be useful to be able to triage test failures into user-defined categories.  Depending on the nature of the failure (and possibly the severity) the failure would be assigned a category.&lt;br /&gt;&lt;br /&gt;The CI server would send  failure notifications to a category-specific list.  For example, system failures would be notified to sys ops, data issues to testers, and code issues to developers.&lt;br /&gt;&lt;br /&gt;Each category would be displayed with a different failure colour, allowing the causes of test failures to be tracked over time.&lt;br /&gt;&lt;br /&gt;For some categories, for example server errors, it may not be worthwhile continuing with the test run. The test runner could potentially be configured to abort the test run dependent on the category of the failure.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Comparison with Pending state&lt;/span&gt;&lt;br /&gt;Many BDD and ATDD tools already model a separate Pending or Unimplemented state - displayed in yellow (Cucumber), or grey (Concordion).  The pending state can be viewed as one of these test failure conditions ("code unavailable").&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Example&lt;/span&gt;&lt;br /&gt;A test could be annotated as follows:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="color: rgb(51, 51, 0);font-size:85%;" &gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;@Triage(nature="SERVER_ERROR", exception=HostUnavailableException.class)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;public class ArrearsFixture() {&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;    @Triage(nature="DATA_UNAVAILABLE",exception=NoDataException.class)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;    public Customer findCustomerInArrears(Condition condition) {&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;        ....&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 51);font-family:courier new;" &gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;On Hudson, test failures might show up as:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_EpHkfjCuniM/TFNjEdradhI/AAAAAAAAAYw/zh2lS0ZwT0E/s1600/temp.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 271px;" src="http://1.bp.blogspot.com/_EpHkfjCuniM/TFNjEdradhI/AAAAAAAAAYw/zh2lS0ZwT0E/s400/temp.jpg" alt="" id="BLOGGER_PHOTO_ID_5499848498286851602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;clearly showing that there is an ongoing issue with server errors, impacting on the team's ability to adequately test the system. Intermittent data-releated issues are also causing some tests to be unrunnable.&lt;br /&gt;&lt;br /&gt;I'm not aware of any test tools/frameworks currently offering this capability.  Does anyone know of anything similar?&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;1. This topic was discussed on the &lt;a href="http://groups.google.com/group/illegalargument/browse_thread/thread/29a88f37a60b4bd3/2ba3fa28eb6f4ad3"&gt;Illegal Argument list&lt;/a&gt;.  I liked Mark Derricutt's point:&lt;br /&gt;&lt;blockquote&gt;It's my thought that the finer grained reporting you CAN get the better,&lt;br /&gt; whether you make use of it depends on the project and problem space.&lt;br /&gt;  &lt;p&gt;After all - Exception is good enough to be thrown for all errors right?&lt;br /&gt;IllegalArgumentException, IOException, FileNotFoundException are all rather&lt;br /&gt;"controversial, mean many things to many people, and cause inconsistency and&lt;br /&gt;confusion" - but we need that differentiation of exceptions to separate out&lt;br /&gt;a chain of responsibility.&lt;br /&gt;&lt;/p&gt; We know this in our code, but I think we also need this for our&lt;br /&gt; builds/tests. &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-586246822075472145?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/iHgfzi2j8Cc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/586246822075472145/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=586246822075472145" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/586246822075472145?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/586246822075472145?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/iHgfzi2j8Cc/triaging-test-failures.html" title="Triaging test failures" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_EpHkfjCuniM/TFNjEdradhI/AAAAAAAAAYw/zh2lS0ZwT0E/s72-c/temp.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2010/06/triaging-test-failures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkADQns-fSp7ImA9Wx9TFE0.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-5116987859584784345</id><published>2009-11-05T16:02:00.008+13:00</published><updated>2010-11-22T17:39:33.555+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-22T17:39:33.555+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="FEST" /><category scheme="http://www.blogger.com/atom/ns#" term="Concordion" /><title>Creating Executable Specifications for Swing apps with Concordion and FEST</title><content type="html">The &lt;a href="http://docs.codehaus.org/display/FEST/Swing+Module"&gt;FEST Swing Module&lt;/a&gt; provides an easy-to-use API for testing Java applications that use the Swing GUI toolkit.&lt;br /&gt;&lt;br /&gt;For a recent project, we created executable specifications with &lt;a href="http://concordion.org/"&gt;Concordion&lt;/a&gt; that used FEST as a driver for the desktop Swing-based GUI.  The combination was very powerful, resulting in readable specifications of the system behaviour that invoked end-to-end tests through the GUI.&lt;br /&gt;&lt;br /&gt;Here are some tips on using FEST effectively with Concordion:&lt;br /&gt;&lt;br /&gt;1) Use FEST's built-in checks that all of the GUI updates are &lt;a href="http://docs.codehaus.org/display/FEST/Testing+that+access+to+Swing+components+is+done+in+the+EDT"&gt;performed in the Event Dispatch Thread&lt;/a&gt; (EDT):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  @BeforeClass&lt;br /&gt;  public static void setUpEDTChecks() {&lt;br /&gt;      FailOnThreadViolationRepaintManager.install();&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2) Install a big red switch. When running the tests locally, they're going to take over your desktop. Use FEST's &lt;a href="http://docs.codehaus.org/display/FEST/Interrupting+Running+Tests"&gt;EmergencyAbortListener&lt;/a&gt; to allow you to terminate the GUI using ctrl - shift - A:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  @BeforeClass&lt;br /&gt;  public static void setUpAbortListener() {&lt;br /&gt;      listener = EmergencyAbortListener.registerInToolkit();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @AfterClass&lt;br /&gt;  public static void tearDownAbortListener() {&lt;br /&gt;      listener.unregister();&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3) Create your user interface once for all tests:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   public static UserInterface getInstance() {&lt;br /&gt;       if (instance == null) {&lt;br /&gt;           instance = new UserInterface();&lt;br /&gt;       }&lt;br /&gt;       return instance;&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;4)) Create your window within the Event Dispatching Thread (EDT)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  private FrameFixture window;&lt;br /&gt;&lt;br /&gt;   private UserInterface() {      &lt;br /&gt;       GuiActionRunner.execute(new GuiTask() {&lt;br /&gt;           public void executeInEDT() {&lt;br /&gt;               JFrame frame = ... // code to create main frame&lt;br /&gt;               window = new FrameFixture(frame);&lt;br /&gt;               setLookAndFeel();&lt;br /&gt;           }&lt;br /&gt;       });&lt;br /&gt;       window.show();    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;5) Slow the tests down when demonstrating to users, so they can see what's happening:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   public void slowDown() {&lt;br /&gt;      window.robot.settings().delayBetweenEvents(500);&lt;br /&gt;      window.robot.settings().eventPostingDelay(500);&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;6) When using FEST's assertions, you can allow assertion failures to bubble up to Concordion as AssertionErrors.  However, you will get more readable Concordion output if you translate them.  For example, with the following Concordion specification:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   &amp;lt;p&gt;Only things of type 'Thing One' should be prioritised....&amp;lt;/p&gt;&lt;br /&gt;   &amp;lt;table concordion:execute="#prioritised =&lt;br /&gt;           isPrioritised(#thingType)"&gt;&lt;br /&gt;       &amp;lt;tr&gt;&amp;lt;th concordion:set="#thingType"&gt;Thing Type&amp;lt;/th&gt;&lt;br /&gt;           &amp;lt;th concordion:assertEquals="#prioritised"&gt;Prioritised&amp;lt;/th&gt;&amp;lt;/tr&gt;&lt;br /&gt;       &amp;lt;tr&gt;&amp;lt;td&gt;Thing One&amp;lt;/td&gt;&amp;lt;td&gt;Yes&amp;lt;/td&gt;&amp;lt;/tr&gt;&lt;br /&gt;       &amp;lt;tr&gt;&amp;lt;td&gt;Thing Two&amp;lt;/td&gt;&amp;lt;td&gt;No&amp;lt;/td&gt;&amp;lt;/tr&gt;&lt;br /&gt;   &amp;lt;/table&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;, the corresponding fixture can be written as:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   public String isPrioritised(String thingType) {&lt;br /&gt;       doStuffWithThing(thingType);&lt;br /&gt;       return userInterface.isPrioritised(request) ? "Yes" : "No";&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;, and integrated with Fest using:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;   public Boolean isPrioritised(Thing thing) {&lt;br /&gt;       int rowIndex = getRowIndex(thing);&lt;br /&gt;       try {&lt;br /&gt;           table.fontAt(row(rowIndex).column(col)).requireBold();&lt;br /&gt;       } catch (AssertionError e) {&lt;br /&gt;           return false;&lt;br /&gt;       }&lt;br /&gt;       return true;&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;7) Follow all of the usual &lt;a href="http://www.concordion.org/Technique.html"&gt;hints and tips&lt;/a&gt; for creating Concordion specifications. In particular:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Write Specifications not Scripts.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Don't mention the GUI.  Your specification should be independent of the implementation.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Evolve a domain-specific language&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I'll write a follow-up to describe how we got these tests running in our Continuous Integration server.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-5116987859584784345?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/hN99XDdhodI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/5116987859584784345/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=5116987859584784345" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/5116987859584784345?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/5116987859584784345?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/hN99XDdhodI/creating-executable-specifications-for.html" title="Creating Executable Specifications for Swing apps with Concordion and FEST" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2009/11/creating-executable-specifications-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EASX88cCp7ImA9WxJVFkw.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-141215597634084037</id><published>2009-07-03T23:08:00.003+12:00</published><updated>2009-07-03T23:14:08.178+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-03T23:14:08.178+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="CITCON" /><title>Correction about Greenpepper</title><content type="html">In my recent APN &lt;a href="http://aboutit.co.nz/APN/APNWLGAgileAcceptanceTestingv1%5B1092kb%5D.pdf"&gt;presentation&lt;/a&gt; on Agile Acceptance Testing, I stated that &lt;a href="http://www.atlassian.com/"&gt;Atlassian&lt;/a&gt; had bought &lt;a href="http://www.greenpeppersoftware.com/en/"&gt;Greenpepper&lt;/a&gt; (the tool). &lt;br /&gt;&lt;br /&gt;Well I was wrong.  I showed some of the slides at &lt;a href="http://www.citconf.com/"&gt;CITCON ANZ&lt;/a&gt; last weekend, and the Atlassian guys corrected me that they had bought Greenhopper (the tool) from Greenpepper (the company), but not Greenpepper (the tool).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-141215597634084037?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/zqLJt9a-WSw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/141215597634084037/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=141215597634084037" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/141215597634084037?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/141215597634084037?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/zqLJt9a-WSw/correction-about-greenpepper_03.html" title="Correction about Greenpepper" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2009/07/correction-about-greenpepper_03.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0YNRX07fSp7ImA9Wx9VGU4.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-778337221301963618</id><published>2009-06-24T09:45:00.015+12:00</published><updated>2011-02-06T09:53:14.305+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-06T09:53:14.305+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="Concordion" /><title>What types of application can I test with Concordion?</title><content type="html">Following on from my Agile Acceptance Testing presentation at the &lt;a href="http://www.agileprofessionals.net/"&gt;APN&lt;/a&gt; on Monday, there were some questions about what types of application you could test with Concordion.&lt;br /&gt;&lt;br /&gt;Concordion itself is a test framework which runs your executable specifications using test fixtures.&lt;br /&gt;&lt;br /&gt;The executable specifications are written in HTML and are  independent of any programming language.  The same executable specifications can be used with &lt;a href="http://concordion.org/"&gt;Concordion&lt;/a&gt; for Java, or &lt;a href="http://code.google.com/p/concordion-net/"&gt;Concordion.NET&lt;/a&gt;.  (and a subset of the syntax can be used with the &lt;a href="http://ruby-concordion.rubyforge.org/"&gt;Ruby&lt;/a&gt; and &lt;a href="http://code.google.com/p/pyconcordion/"&gt;Python&lt;/a&gt; ports of Concordion).&lt;br /&gt;&lt;br /&gt;The test fixtures are written in a programming language.  For the Java version of Concordion, the test fixtures are written as JUnit classes. (However they don't include test methods and don't tend to include assertions, since these are defined in the executable specification.)&lt;br /&gt;&lt;br /&gt;There are 2 main ways in which Concordion is used.  Either through the User Interface, or "under the covers" to the application or services layer.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_EpHkfjCuniM/SkFR9bIzKCI/AAAAAAAAAS0/qjNZ6XHvkXc/s1600-h/TestArchitecture.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 244px;" src="http://4.bp.blogspot.com/_EpHkfjCuniM/SkFR9bIzKCI/AAAAAAAAAS0/qjNZ6XHvkXc/s400/TestArchitecture.jpg" alt="" id="BLOGGER_PHOTO_ID_5350647947991394338" border="0" /&gt;&lt;/a&gt;When testing "under the covers" of the application, the test fixture calls into the application code directly.  In this case, the type of application is largely irrelevant.&lt;br /&gt;&lt;br /&gt;When testing through the user interface, you need a Test Driver. Test drivers provide the functionality to find widgets, click buttons, enter text etc.  There are a lot of high quality, open source test drivers available.  Some of these, such as Selenium and SoapUI also come with their own test frameworks, but can be used purely as test drivers.  The test drivers I have used with Concordion are:&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;br /&gt; &lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;   &lt;td&gt;&lt;b&gt;Application Type&lt;/b&gt;&lt;/td&gt;&lt;br /&gt;   &lt;td&gt;&lt;b&gt;Test Driver&lt;/b&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt; &lt;tr&gt;&lt;br /&gt;   &lt;td&gt;Web Application&lt;/td&gt;&lt;br /&gt;   &lt;td&gt;&lt;a href="http://code.google.com/p/selenium/wiki/GettingStarted"&gt;WebDriver&lt;/a&gt; and &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt; &lt;tr&gt;&lt;br /&gt;   &lt;td&gt;Swing GUI Application&lt;/td&gt;&lt;br /&gt;   &lt;td&gt;&lt;a href="http://easytesting.org/"&gt;Fest&lt;/a&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt; &lt;tr&gt;&lt;br /&gt;   &lt;td&gt;Web Service&lt;/td&gt;&lt;br /&gt;   &lt;td&gt;&lt;a href="http://www.soapui.org/"&gt;SoapUI&lt;/a&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Additionally there are a lot of test extension libraries available, such as DbUnit and XmlUnit that can be called from your test fixture.&lt;br /&gt;&lt;br /&gt;Concordion is a great framework for running your executable specifications.  When combined with the right driver you can test pretty much any type of application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-778337221301963618?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/DU5efJzqdvI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/778337221301963618/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=778337221301963618" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/778337221301963618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/778337221301963618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/DU5efJzqdvI/what-types-of-application-can-i-test.html" title="What types of application can I test with Concordion?" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_EpHkfjCuniM/SkFR9bIzKCI/AAAAAAAAAS0/qjNZ6XHvkXc/s72-c/TestArchitecture.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2009/06/what-types-of-application-can-i-test.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMFQns8cCp7ImA9WxRREkw.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-7194454200631242263</id><published>2008-09-24T11:57:00.003+12:00</published><updated>2008-09-24T12:00:13.578+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-24T12:00:13.578+12:00</app:edited><title>So what does that make me?</title><content type="html">While tarring up an old file system:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-family: courier new;font-size:100%;" &gt;&lt;/span&gt;&lt;blockquote style="font-style: italic;"&gt;&lt;span style="font-family: courier new;font-size:100%;" &gt;tar: .: implausibly old time stamp 1970-01-01 12:00:00&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-7194454200631242263?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/t8PH0dCH810" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/7194454200631242263/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=7194454200631242263" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/7194454200631242263?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/7194454200631242263?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/t8PH0dCH810/so-what-does-that-make-me.html" title="So what does that make me?" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/09/so-what-does-that-make-me.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAMQ346eCp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-537640348875234656</id><published>2008-09-23T21:18:00.011+12:00</published><updated>2008-09-23T22:46:22.010+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:22.010+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SchemaSpy" /><title>Using SchemaSpy with multiple schema</title><content type="html">A happy coincidence occurred last week.&lt;br /&gt;&lt;br /&gt;One of my current projects has a database with foreign key relationships spanning across two schemas.  I needed to create some database documentation.&lt;br /&gt;&lt;br /&gt;And &lt;a href="http://schemaspy.sourceforge.net/"&gt;SchemaSpy&lt;/a&gt; v4 was in late beta. One of the great new features of v4 is support for multiple schema. John Currier was short of a "real" database to test these changes on.&lt;br /&gt;&lt;br /&gt;John gave me great support in working through a few issues and fixing up a couple of bugs, that have made it into the SchemaSpy 4.0.0 release.&lt;br /&gt;&lt;br /&gt;The result is a new main page showing all of the selected schema:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_EpHkfjCuniM/SNi_RgeZsaI/AAAAAAAAANQ/IGcp0wUfR8s/s1600-h/schema-main.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_EpHkfjCuniM/SNi_RgeZsaI/AAAAAAAAANQ/IGcp0wUfR8s/s320/schema-main.jpg" alt="" id="BLOGGER_PHOTO_ID_5249155673196442018" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Drilling down, the relationship graph now shows related tables in foreign schema:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_EpHkfjCuniM/SNi_shxyEaI/AAAAAAAAANY/S5_UT8fsVfw/s1600-h/schema-reln.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_EpHkfjCuniM/SNi_shxyEaI/AAAAAAAAANY/S5_UT8fsVfw/s320/schema-reln.jpg" alt="" id="BLOGGER_PHOTO_ID_5249156137402634658" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;SchemaSpy does an excellent job of hyperlinking all of the tables, columns and relationships, allowing you to easily navigate around your schema.&lt;br /&gt;&lt;br /&gt;To use the multiple schema feature, add a &lt;span style="font-style: italic;"&gt;-all&lt;/span&gt; parameter to your SchemaSpy command line. With this parameter, SchemaSpy will include all schema except for system tables (which are determined by a &lt;span style="font-style: italic;"&gt;schemaSpec&lt;/span&gt; pattern in the properties file for the specified database type).   To further restrict the schema, add a &lt;span style="font-style: italic;"&gt;-schemaSpec&lt;/span&gt; command line parameter with a regular expression to match the required schema.&lt;br /&gt;&lt;br /&gt;The following ant task instructs SchemaSpy to document the PMH and SHO schema of our DB2 database.&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;      &lt;/span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&amp;lt;java jar="lib/schemaSpy_4.0.0.jar" fork="true"/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-t udbt4"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-host ${db.host}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-port ${db.port}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-db ${database}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-u ${db.userid}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-p ${db.password}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-all"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-schemaSpec (PMH)|(SHO)"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-dp ${DB2_LIB}/db2jcc.jar:${DB2_LIB}/db2jcc_license_cu.jar"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;      &amp;lt;arg line="-o ${reports.data.model.dir}"/&gt;&lt;/span&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;&amp;lt;/java&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-537640348875234656?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/sdsohrDH2x8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/537640348875234656/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=537640348875234656" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/537640348875234656?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/537640348875234656?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/sdsohrDH2x8/using-schemaspy-with-multiple-schema.html" title="Using SchemaSpy with multiple schema" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_EpHkfjCuniM/SNi_RgeZsaI/AAAAAAAAANQ/IGcp0wUfR8s/s72-c/schema-main.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/09/using-schemaspy-with-multiple-schema.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEACRH89cSp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-3039348047999170655</id><published>2008-08-19T17:59:00.003+12:00</published><updated>2008-09-23T22:46:05.169+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:05.169+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="Hudson" /><category scheme="http://www.blogger.com/atom/ns#" term="Crap4J" /><title>Crap4J Hudson update</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_EpHkfjCuniM/SKphrMrU7PI/AAAAAAAAAMo/cIo2wziU2Go/s1600-h/crap-hudson-graph.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_EpHkfjCuniM/SKphrMrU7PI/AAAAAAAAAMo/cIo2wziU2Go/s320/crap-hudson-graph.png" alt="" id="BLOGGER_PHOTO_ID_5236104911536844018" border="0" /&gt;&lt;/a&gt; The last graph I posted didn't really do the &lt;a href="http://hudson.gotdns.com/wiki/display/HUDSON/Crap4J+Plugin"&gt;Crap4J Hudson plugin&lt;/a&gt; justice, especially since Daniel Lindner fixed the bug where %age figures disappeared when Crappyness was &lt; 1%. &lt;br /&gt;&lt;br /&gt;Daniel replied to me at the time that "when your code isn't crap, the Crap4J plugin is".  Well, now the plugin's fixed, I'd better start cutting the crap in my code :-) &lt;br /&gt;&lt;br /&gt;I'll be doing a mini-presentation on Crap4J at the &lt;a href="http://jug.wellington.net.nz/"&gt;Wellington Java User Group meeting&lt;/a&gt; tomorrow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-3039348047999170655?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/wswwglpuO44" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/3039348047999170655/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=3039348047999170655" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3039348047999170655?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3039348047999170655?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/wswwglpuO44/crap4j-hudson-update.html" title="Crap4J Hudson update" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_EpHkfjCuniM/SKphrMrU7PI/AAAAAAAAAMo/cIo2wziU2Go/s72-c/crap-hudson-graph.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/08/crap4j-hudson-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEANRX47cCp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-4265247166134079358</id><published>2008-07-07T21:52:00.002+12:00</published><updated>2008-09-23T22:46:34.008+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:34.008+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="CITCON" /><title>CITCON Melbourne '08</title><content type="html">The Continuous Integration and Testing Conference (CITCON) recently held in Melbourne was full of insights.&lt;br /&gt;&lt;br /&gt;The Friday night session taught me just how many people really don't like Brussel Sprouts and Vegemite (in isolation, though it would make an interesting mix).&lt;br /&gt;&lt;br /&gt;This was the first Open Space conference for the majority of the attendees (including me), and I was blown away by the energy that people brought to the conference, and the number of great sessions that sprung up (see the &lt;a href="http://flickr.com/photos/talios/2624412450/in/set-72157605892492402"&gt;whiteboard&lt;/a&gt; and the &lt;a href="http://www.citconf.com/wiki/index.php?title=CITCONAsiaPacific2008Sessions"&gt;wiki&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Jeremy Wales presented on his experience with Acceptance TDD on a Suncorp project.  This led to some interesting discussions on (not) using the DRY principle with acceptance tests, and having testers involved upfront in designing the tests.&lt;br /&gt;&lt;br /&gt;Other great sessions included Continuous Performance Testing, Show us your Build!, and Using Groovy in Testing.&lt;br /&gt;&lt;br /&gt;I've come away with a few new tools to try (including &lt;a href="http://www.concordion.org/"&gt;Concordion&lt;/a&gt; for writing active specifications, and &lt;a href="http://code.google.com/p/jchav/"&gt;JChav&lt;/a&gt; for charting JMeter results), a renewed focus on writing my tests in Groovy and plenty to read...&lt;br /&gt;&lt;br /&gt;Thanks to all who made the conference happen.  The format was fantastic and I'm already looking forward to next year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-4265247166134079358?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/5JE-0rHGwto" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/4265247166134079358/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=4265247166134079358" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/4265247166134079358?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/4265247166134079358?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/5JE-0rHGwto/citcon-melbourne-08.html" title="CITCON Melbourne '08" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/07/citcon-melbourne-08.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEACRH88eCp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-5290651353073728831</id><published>2008-06-24T19:53:00.009+12:00</published><updated>2008-09-23T22:46:05.170+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:05.170+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><category scheme="http://www.blogger.com/atom/ns#" term="Hudson" /><category scheme="http://www.blogger.com/atom/ns#" term="Crap4J" /><title>Crap4J, Hudson and Windows</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_EpHkfjCuniM/SGC3hNz9MAI/AAAAAAAAAMc/Gsyr0lWYdRk/s1600-h/blog3.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_EpHkfjCuniM/SGC3hNz9MAI/AAAAAAAAAMc/Gsyr0lWYdRk/s320/blog3.PNG" alt="" id="BLOGGER_PHOTO_ID_5215370149766180866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;There is now a Crap4J plugin for the &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt; continuous integration server, thanks to Daniel Lindner.&lt;br /&gt;&lt;br /&gt;For those of you unfamiliar with &lt;a href="http://www.crap4j.org/"&gt;Crap4J&lt;/a&gt;, it is a metric that "&lt;span style="font-style: italic;"&gt;combines cyclomatic complexity and code coverage from automated tests to help you identify code that might be particularly difficult to understand, test, or maintain&lt;/span&gt;".&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://hudson.gotdns.com/wiki/display/HUDSON/Crap4J+Plugin"&gt;Hudson Crap4J plugin&lt;/a&gt; maintains "Crappyness Trends", which is a feature missing from the standard Crap4J reports.  It also shows details of any methods that exceed the crap threshold.&lt;br /&gt;&lt;br /&gt;In order to use the plugin, you must first download the &lt;a href="http://www.crap4j.org/ant_usage.html"&gt;Crap4J Ant task&lt;/a&gt; and integrate it into your Ant build file.  If you're running Windows, this is where you're likely to run into a road-block. On Windows, a &lt;a href="http://trac.crap4j.org/ticket/48"&gt;bug&lt;/a&gt; stops the Ant task from figuring out the Crap4J home.  The workaround is to set the ANT_OPTS environment variable:&lt;blockquote&gt;set ANT_OPTS="-DCRAP4J_HOME=&lt;span style="font-style: italic;"&gt;c:\java\tools\crap4j-ant&lt;/span&gt;"&lt;/blockquote&gt;, where &lt;span style="font-style: italic;"&gt;c:\java\tools\crap4j-ant&lt;/span&gt; is the location of your Crap4J ant tasks.&lt;br /&gt;&lt;br /&gt;Once your Ant build is producing the Crap reports, you're ready to integrate it into Hudson.&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_EpHkfjCuniM/SGC1JGWrtAI/AAAAAAAAAMM/Pn_r6c-7ZvM/s1600-h/blog.PNG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_EpHkfjCuniM/SGC1JGWrtAI/AAAAAAAAAMM/Pn_r6c-7ZvM/s200/blog.PNG" alt="" id="BLOGGER_PHOTO_ID_5215367536424236034" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After downloading and installing the plugin, you'll need to add the Ant target that is running the Crap4J task. In the example, I've set up a crap4j target.&lt;br /&gt;&lt;br /&gt;Then, on Windows, you'll need to add the Ant options to detect the Crap4J home.  Click on the "Advanced..." button in the &lt;span style="font-weight: bold;"&gt;Build&lt;/span&gt; section.  In the Java Options, enter &lt;blockquote&gt;-DCRAP4J_HOME=&lt;span style="font-style: italic;"&gt;c:\java\tools\crap4j-ant&lt;/span&gt;&lt;/blockquote&gt;, where &lt;span style="font-style: italic;"&gt;c:\java\tools\crap4j-ant&lt;/span&gt; is the location of your Crap4J ant tasks.&lt;br /&gt;&lt;br /&gt;Then, in the&lt;span style="font-weight: bold;"&gt; Post-build Actions&lt;/span&gt; section, specify the location of&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_EpHkfjCuniM/SGC2MNtYzuI/AAAAAAAAAMU/zim1R5xe1ZU/s1600-h/blog1.PNG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_EpHkfjCuniM/SGC2MNtYzuI/AAAAAAAAAMU/zim1R5xe1ZU/s200/blog1.PNG" alt="" id="BLOGGER_PHOTO_ID_5215368689449750242" border="0" /&gt;&lt;/a&gt; the output from your Crap4J ant task.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The next time Hudson runs your build, look for the toilet roll on the left of the dashboard for your Crap details.&lt;br /&gt;&lt;br /&gt;A few comments on the current plugin (currently at v0.2):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It would be great to be able to adjust the CRAP threshold. The default threshold of 30 is really too high for new code, and I typically set it to 15.&lt;/li&gt;&lt;li&gt;I'd also love to be able to view the complexity, coverage and CRAP scores for all methods, not just the scores for the methods over the CRAP threshold.&lt;/li&gt;&lt;li&gt;When the crap method percentage is less than 1, the Crappyness Trend chart does not show any %age figures on the scale.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-5290651353073728831?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/d2prjCZWqnI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/5290651353073728831/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=5290651353073728831" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/5290651353073728831?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/5290651353073728831?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/d2prjCZWqnI/crap4j-hudson-and-windows.html" title="Crap4J, Hudson and Windows" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_EpHkfjCuniM/SGC3hNz9MAI/AAAAAAAAAMc/Gsyr0lWYdRk/s72-c/blog3.PNG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/06/crap4j-hudson-and-windows.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEACRH88eCp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-3729374959289513291</id><published>2008-06-23T16:46:00.006+12:00</published><updated>2008-09-23T22:46:05.170+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:05.170+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><title>Static imports for Hamcrest and Theories</title><content type="html">The Hamcrest and Theories features of JUnit 4.4 rely on a number of static imports.&lt;br /&gt;&lt;br /&gt;Code completion for static imports is tricky.  For example, if I want to use the &lt;i&gt;both&lt;/i&gt; Matcher, I firstly have to remember that it is in the JUnitMatchers class, type in &lt;i&gt;JUnitMatchers&lt;/i&gt; (or &lt;i&gt;JUM&lt;/i&gt;), then &lt;i&gt;ctrl-space&lt;/i&gt; to get code completion to fill in the import, then type in &lt;i&gt;.both&lt;/i&gt; and remove the &lt;i&gt;JUnitMatchers.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;As mentioned in the &lt;a href="http://junit.sourceforge.net/doc/ReleaseNotes4.4.html"&gt;JUnit 4.4 release notes&lt;/a&gt;, Eclipse provides a Favorites preference that automatically includes your favourite classes in code assist.  For example, after setting up JUnitMatchers in your Favorites, you can type in &lt;i&gt;both&lt;/i&gt;, &lt;i&gt;ctrl-space&lt;/i&gt;, and Eclipse will import &lt;i&gt;JUnitMatchers.both&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_EpHkfjCuniM/SF87y_hKaII/AAAAAAAAAL8/PHDhz-ds2EA/s1600-h/eclipse-prefs.PNG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_EpHkfjCuniM/SF87y_hKaII/AAAAAAAAAL8/PHDhz-ds2EA/s400/eclipse-prefs.PNG" alt="" id="BLOGGER_PHOTO_ID_5214952640748349570" border="0" /&gt;&lt;/a&gt;Adding the static types shown here to your Eclipse Favorites will make your JUnit 4.4 journey a lot smoother.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS. The &lt;i&gt;both&lt;/i&gt; Matcher allows you to create assertions such as:&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;assertThat(e.getMessage(), both(startsWith("Invalid environment")).and(containsString(environmentName));&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;as opposed to the more common:&lt;br /&gt;&lt;blockquote  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;assertThat(e.getMessage(), allOf(startsWith("Invalid environment"), containsString(environmentName)));&lt;br /&gt; &lt;/span&gt;&lt;/blockquote&gt;It's debatable which is cleaner.  The top statement reads closer to the English language, but is longer, more complex to construct and can't have additional matchers added in the same way that &lt;i&gt;allOf&lt;/i&gt; can.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="file:///C:/DOCUME%7E1/nigel/LOCALS%7E1/Temp/moz-screenshot.jpg" alt="" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-3729374959289513291?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/pkzqvsimCI4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/3729374959289513291/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=3729374959289513291" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3729374959289513291?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/3729374959289513291?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/pkzqvsimCI4/static-imports-for-hamcrest-and.html" title="Static imports for Hamcrest and Theories" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_EpHkfjCuniM/SF87y_hKaII/AAAAAAAAAL8/PHDhz-ds2EA/s72-c/eclipse-prefs.PNG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/06/static-imports-for-hamcrest-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEACRH88eSp7ImA9WxRREUs.&quot;"><id>tag:blogger.com,1999:blog-2959792190087040511.post-1344395563966320943</id><published>2008-06-01T02:27:00.001+12:00</published><updated>2008-09-23T22:46:05.171+12:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-23T22:46:05.171+12:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Code Quality" /><title>On the eradication of software defects</title><content type="html">I loved Andy Glover's &lt;a href="http://thediscoblog.com/2008/05/29/the-business-model-or-lack-thereof-of-selling-unit-testing/"&gt;hip comparison of mousetraps to testing tools and mice to defects&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I live in a country that had no mammals, other than a few bats, and no software defects, until man arrived around 1000 years ago. The introduced mammals have had a devastating effect on the &lt;a href="http://en.wikipedia.org/wiki/Biodiversity_of_New_Zealand"&gt;native biodiversity&lt;/a&gt;. A desire to protect the remaining species has led New Zealand to be world-renowned in the removal of introduced species. I wish I could say the same for software defects.&lt;br /&gt;&lt;br /&gt;K.P. Brown and G.H. Sherley, in their &lt;a href="http://issg.appfa.auckland.ac.nz/database/species/reference_files/TURTID/Brown.pdf"&gt;paper&lt;/a&gt; describing the eradication of &lt;a href="http://en.wikipedia.org/wiki/Possum"&gt;possums &lt;/a&gt;on &lt;a href="http://en.wikipedia.org/wiki/Kapiti_Island"&gt;Kapiti Island&lt;/a&gt;, show the following success rates for different phases of the trapping programme.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_EpHkfjCuniM/SEFGR8MJA8I/AAAAAAAAALc/Zip8ajNdCbA/s1600-h/possum.GIF"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_EpHkfjCuniM/SEFGR8MJA8I/AAAAAAAAALc/Zip8ajNdCbA/s400/possum.GIF" alt="" id="BLOGGER_PHOTO_ID_5206519918245774274" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The first phase, Commercial Trapping, resulted in a 24% success rate of trapping possums.  Once trapping stopped being a commercial proposition, 4 trappers were paid to set up to 1500 traps per night and had a success rate of 0.7%.  The final Eradication phase introduced dog teams in addition to the trapping program.  At this stage, the trapping success rate was down to 0.007%, and dogs caught the remaining 32 trap-shy possums.&lt;br /&gt;&lt;br /&gt;I suspect that similar success rates would be encountered in finding software defects.  A good unit testing program, at a cost of 3-4 lines of test code for each line of code under test, might  yield a success rate similar to Commercial Trapping.  Some of the remaining bugs would be detected in intensive system testing.  Many escape undetected into production. &lt;br /&gt;&lt;br /&gt;Most development shops don't even make it through the Commercial Trapping phase.&lt;br /&gt;&lt;br /&gt;The true cost of the $1 mouse trap is in the time, and cheese, taken to set it. &lt;br /&gt;&lt;br /&gt;Agitar changed the equation.  At the press of a button, Agitator would set the traps for me hundreds if not thousands of times.  This would drive my unit testing past the Commercial Trapping into Intensive Control territory.  With a bit more tuning, I could even start dreaming of bug eradication.&lt;br /&gt;&lt;br /&gt;My hat goes off to those hardy souls who rid Kapiti Island of possums (and mice, rats, stoats etc.) and to the folks at Agitar who wanted to make it easier for us to eradicate those damn electronic vermin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2959792190087040511-1344395563966320943?l=tutansblog.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/tutansblog/~4/39FZ77IrIWo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tutansblog.blogspot.com/feeds/1344395563966320943/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=2959792190087040511&amp;postID=1344395563966320943" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/1344395563966320943?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2959792190087040511/posts/default/1344395563966320943?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/tutansblog/~3/39FZ77IrIWo/on-eradication-of-software-defects.html" title="On the eradication of software defects" /><author><name>Nigel Charman</name><uri>http://www.blogger.com/profile/14935723589029857874</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_EpHkfjCuniM/SEFGR8MJA8I/AAAAAAAAALc/Zip8ajNdCbA/s72-c/possum.GIF" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tutansblog.blogspot.com/2008/06/on-eradication-of-software-defects.html</feedburner:origLink></entry></feed>

