Archive for November, 2009

A Day in the Life of a Scrum Team

An interesting time-lapse video. Neat to pick out the interactions revealed. Wish they hadn’t sometimes hammed it up for the camera.

Big UI changes and their effect on tests

On the agile-testing list, I recently made this claim:

I changed the UI of an app and so… I had to rewrite the UI code and the tests/checks of the UI code. It might seem the checks were worthless during the rewrite (and in 1997, I would have thought so). But it turns out that all (or almost all) of those checks contained an idea that needed to be preserved in the new interface. They were reminders of important things to think about, and that (it seemed to me) repaid the cost of rewriting them.

That was a big surprise to me.

Here are more details. Here’s a bit of the current UI:

Animal display

In this app, you are reserving animals so that professors can demonstrate procedures to medical students (or let the students practice procedures). You put animals and procedures together by clicking on them. In this case, I’ve chosen “Good Morning Sunshine”.

The next thing I might do is pick a procedure, say abdominocentesis. However, there are “business rules” that forbid you from performing that procedure on an animal more than once every two weeks. Suppose Good Morning Sunshine has already been scheduled for abdominocentesis one week before the lab session I’m trying to schedule. In that case, if I clicked “abdominocentesis” (in a list you can’t see), Good Morning Sunshine would disappear from the left (”used”) list. Any other animals excluded by the business rule would disappear from the right (”available”) list.

Fine. Here’s a question: what should happen if I now unchoose abdominocentesis? Good Morning Sunshine is now available - but should she be re-chosen? As it turns out, the desired behavior is for her not to be re-chosen. She’ll appear in the right list, not the left.

Below I show the test that currently describes this. A few words of warning: The UI is written in Objective-J, a language built on top of Javascript. I had to build my own mocking framework for it. Although this test doesn’t use mocks, it’s written using that (idiosyncratic) framework.

The following will be a little easier to read if you know that sut (tester jargon for “system under test”) is the controller that manages the lists. “Withholding objects” is how the rest of the front-end tells the sut which animals are excluded by the current set of chosen procedures. The test first withholds two animals, then withdraws that (by withholding nothing).

- (void) testWithholdingAnNamedObjectDoes_Not_RechooseItIfItReappears
{
 [scenario
 previousAction: function() {
     [sut allPossibleObjects: [betsy, spike, fang]];
     [self simulateChoiceOf: [spike, fang]];
     [sut withholdNamedObjects: [betsy, fang]];
     [self assert: [] equals: [sut.available content]];
     [self assert: [spike] equals: [sut.used content]];
   }
 testAction: function() {
     [sut withholdNamedObjects: []];
   }
 andSo: function() {
     [self assert: [betsy, fang] equals: [sut.available content]];
     [self assert: [spike] equals: [sut.used content]];
   }];
}

But when I first started thinking about this issue, I wasn’t using a click-to-move style. Instead, you dragged from one list to another (and the lists were together in one panel, instead of being split among two). The earlier test for that behavior was, however, almost the same. There’s one changed method, I’d apparently done a global search-and-replace on animal names, and in the earlier version I hadn’t generalized this behavior to apply to procedures as well as animals:

- (void) testWithholdingAnAnimalDoes_Not_RechooseItIfItReappears
{
 [scenario
 previousAction: function() {
     [sut beginUsing: [betty, dave, fred]];
     [self simulateChoiceOf: [dave, fred]];
     [sut withholdAnimals: [betty, fred]];
     [self assert: [] equals: [sut.available content]];
     [self assert: [dave] equals: [sut.used content]];
   }
 testAction: function() {
     [sut withholdAnimals: []];
   }
 andSo: function() {
     [self assert: [betty, fred] equals: [sut.available content]];
     [self assert: [dave] equals: [sut.used content]];
   }];
}

The important thing here is that the “user experience concept” is the same. In fact, that concept even predates this test. In an earlier UI, there weren’t two lists. Instead, each animal had a checkbox next to it. You chose and unchose animals by clicking the checkbox. (Unavailable animals disappeared, just as they do now.)

I’ve reached back in the version control history for that behavior’s test. It’s cruder, because I was still passing strings and dictionaries around. (It was only later that the app grew model objects.) Nevertheless, the structure and intent are the same. In this sense, the test represents a “frozen decision” that’s survived the life of the project (so far).

- (void) testWithholdingAnAnimalErasesItsCheckmarkIfItReappears
{
 [scenario
 previousAction: function() {
     [sut beginUsingAnimals: [”alpha“,  delta“, betty“]
                withKindMap: {”alpha:”akind, delta:”dkind, betty:”bkind}];

     [self alreadySelected: [”betty“, delta“]];
     [sut withholdAnimals: [’betty‘]];
     [self animalTableWillContainNames: [”alpha (akind)“, delta (dkind)“]];
     [self animalTableWillHaveCorrespondingChecks: [NO, YES]];
   }
 testAction: function() {
     [sut withholdAnimals: []];
   }
 andSo: function() {
     [self animalTableWillContainNames: [”alpha (akind)“, betty (bkind)“, delta (dkind)“]];
      [self animalTableWillHaveCorrespondingChecks: [NO, NO, YES]];
   }];
}