Wednesday, July 18, 2007

Fit from a Developer's Perspective

In an earlier post, Mandy wrote about the benefits of using Fit from a business owner’s perspective. I’d like to add to that and describe some of the benefits that I’ve seen as a developer working with Fit from deep in the trenches.

In our development process, we attempt to create Fit tests for new user stories up front in collaboration with business owners (let’s call this “Fit-driven development” - because we love the “X-driven” paradigms). This has several advantages for developers. First, we learn the language and rules of the business. This is crucial to any attempt at domain-driven design (see?), in which we carry the language and concepts of the business domain deep into our applications. Second, it gets us collaborating with the business owners on a day to day basis beyond the bounds of the SCRUM meeting. This builds knowledge of each other’s worlds and, more importantly, empathy, which is key to a building a positive working relationship. Third, we add to the test coverage in our system – tests that are usually at a level higher than unit tests, like integration or acceptance tests. All of this contributes to being able to get it right the first time.

Now, to be honest, we don’t always develop Fit-first but when we don’t I often find myself wishing that we had – for example, the time when I discovered that the language I used in my code was woefully inconsistent with the language the business uses, or the time when we implemented a core piece of logic and had to rewrite it when we learned that we had misinterpreted the partially-erased scribble on the whiteboard - shock! As a developer who must admit to rewriting code too frequently, tools, like Fit, that help to get it right the first time are whole-heartedly welcomed.

Wednesday, July 11, 2007

Extending Fit with a New Fixture

Fit is a very flexible tool for testing due in large part to the pre-defined fixture types (ActionFixture, ColumnFixture, RowFixture, fitlibrary’s DoFixture, etc.) This flexibility means it’s usually possible to write test tables in a way that is intuitive and easy to read. However, we’ve come across a situation where the test tables we want to write aren’t executable by any of Fit’s built-in fixture types.

The feature we’re working with is a Fit HTML table builder. Our test provides a fixture classname as input and expects a Fit HTML table as output. Here’s an example of what we wanted to test:



Unfortunately, when we ran this test using a ColumnFixture, Fit interpreted the nested table as more parsable cells rather than expected HTML output. And, while there are other ways to structure the test, such as using raw HTML, none of them seem very easy to work with.

What we really need is a new type of fixture, which interprets the table data in the last column as HTML content to be compared with the result of the fixture’s verification method (in the case above, table()).

public class HtmlTableFixture extends ColumnFixture {

@Override
public void check(Parse cell, TypeAdapter adapter) {
String expected = cell.body;
try {
String result = (String) adapter.get();
if (expected.equals(result)) {
right(cell);
} else {
wrong(cell, result);
}
} catch (Exception e) {
exception(cell, e);
}
}

@Override
protected Binding createBinding(int column, Parse heads)
throws Throwable {
Binding binding = super.createBinding(column, heads);
if (column == columnBindings.length - 1) {
binding = new HtmlTableBinding(binding);
}
return binding;
}

private static final class HtmlTableBinding
extends Binding {

private final Binding internalBinding;

private HtmlTableBinding(Binding binding) {
this.internalBinding = binding;
}

@Override
public void doCell(Fixture fixture, Parse cell)
throws Throwable {
Unparse unparse = new Unparse(cell.parts);
String html = unparse.text;
cell.body = html;
internalBinding.doCell(fixture, cell);
}
}
}


Our fixture creates a custom binding, called HtmlTableBinding, which gets bound to the last cell in each row. This binding unparses the cell’s contents and stores them in the body of the cell’s Parse object. When the cell is later evaluated by check(), our fixture gets the expected value from the body of the cell's Parse object and compares it with the result from executing the method (via the TypeAdapter).

Running the test now gives us:



So, Fit is not only a flexible testing tool, it's also easily extensible for those corner cases where you might find that it doesn't give you exactly what you want.


* To understand why we need to unparse it helps to know how Fit reads HTML tables. When Fit is run on a file, the first thing it does is create a model of all of the table information, which it stores in a composite object called Parse. Each part of a table (i.e., <table>, <tr>, or <td>) is a nested Parse object within the composite. Unparsing is achieved by traversing the composite Parse and appending the HTML parts to a buffer.

public class Unparse {

public String text;

public Unparse(Parse parse) {
text = unparse(parse);
}

private String unparse(Parse parse) {
StringBuffer sb = new StringBuffer();
sb.append(parse.tag);
if (parse.body != null) {
sb.append(parse.body);
}
if (parse.parts != null) {
sb.append(unparse(parse.parts));
}
sb.append(parse.end);
if (parse.more != null) {
sb.append(unparse(parse.more));
}
return sb.toString();
}
}

Tuesday, July 10, 2007

Agile 2007 Conference

I recently found out that Stephen, Michael, Askhat and I are attending the Agile 2007 conference in Washington, D.C. August 13-16th. We hope to bring back some fresh ideas about Agile software development and lots of blog post material. Come say hi at the Luxoft booth!

Friday, July 6, 2007

Why We Scrum

I read an article today complaining that scrum was just another useless meeting and that the time would be put to better use speaking to the other developers about design issues one on one. I tend to disagree.
At a previous company, during our waterfall days, product managers tended throw requirements over the wall as they were busy. As a tester, I often found large discrepancies between my interpretation of the requirements and what the developer had coded into the product. On occasion I had to strong-arm developers into the product manager's office so we could come to a common understanding of a feature.
We had product development offices in Boston and Vancouver, so often the product manager, tester and developer were not co-located and had never met.
Scrum helped alot. When daily scrum meetings started up, we met our cross-department distributed team (face-to-face or over phone/video conference) and had a daily opportunity to discuss all the little questions and blockages that come up as you code and test. We came to a common understanding daily, and if we got astray from the feature vision the next day, it was still recoverable.
In conclusion, scrum breaks down the barrier of departments and lack of co-location, and gets a team communicating and collaborating. Cause many brains really are better than one.