Wednesday, November 19, 2008

Test pattern

File this under note to self. Say you have a method to test, and inside it looks something like:

public class Foo
{
public bool Bar()
{
return A && B && C && D == "https";
}
}

Meaning, your method has several dimensions of possible inputs, and only one or a handful of acceptable states. Obviously, if the function really is as simple as that, you might not even want a test; static inspection might suffice. But suppose that the evaluation of A, B, and C is a little more complex or expensive, and suppose that there are, say, 3 different acceptable combinations among the full permuation of inputs.

Further suppose that A..D are each enumerable domains. (The actual use case that made me think of this was something like: A is "whether a page requires SSL", B is "the user is authenticated", C is "a certain control is visible", and D is "the protocol on the page request".)

What should the test look like?

The code that began to test-drive the implementation started out like this:

[Test]
public void Bar()
{
Foo foo = new Foo() { A = true, B = true, C = true, D = Uri.Https };
Assert.That(foo.Bar(), Is.True);
// cases 2 and 3 not shown but expect true

// everything else expects false
foo.A = false;
Assert.That(foo.Bar(), Is.False);
// tedious and brittle cut-and-paste looms if not careful...
}

This is the sort of situation you get into with TDD and Simplest Thing That Could Possibly Work. Having plucked the low-hanging fruit in the first few passes, you then need to cover something like "all the other situations". You're tempted to write something like:

[Test]
public void Bar()
{
foreach(bool a in new[] {true, false})
foreach(bool b in new[] {true, false})
foreach(bool c in new[] {true, false})
foreach(string scheme in new[] {Uri.UriSchemeHttp, Uri.UriSchemeHttps, Uri.UriSchemeFile})
{
Foo foo = new Foo() { A = a, B = a, C = a, D = scheme};
Assert.That(foo.Bar(), Is.EqualTo(a && b && c && d == Uri.Https));
// okay, this only covers one of the three acceptance cases; you get the idea, though
}
}

In other words, your verification looks suspiciously like your implementation. Nine times out of ten, that will work. But every so often you discover that you've overlooked some aspect of the problem, and your mirror-image test code didn't identify the discrepancy because it was so similar to the implementation that both had the same bug. (For example: maybe the evaluation of A, B, and C can have side effects that create a subtle a temporal dependency in the order of evaluation, and by duplicating the SUT logic in the test, you never exercise alternative paths.) In general, I feel much safer if the test code takes a different route over the problem space than the SUT does.

You could go table-driven, but then you've got a table to maintain, and how do you know the table is correct? (Read: exhaustive.) Really, you want to codegen the table, which is why I actually like the nested foreach() blocks that iterate over explicit sets in the second version. The loops are clear, terse, and exhaustive. I'd like to keep that portion of the structure while having something that is logically equivalent without being codewise identical.

So, here's the pattern I prefer:

[Test]
public void Bar()
{
// explicitly test each of the small number of outliers
Foo foo = new Foo() { A = true, B = true, C = true, D = Uri.UriSchemeHttp };
Assert.That(foo.Bar(), Is.True);
// repeat for the two other affirmative cases

int trues;
foreach(bool a in new[] {true, false})
foreach(bool b in new[] {true, false})
foreach(bool c in new[] {true, false})
foreach(string scheme in new[] {Uri.UriSchemeHttp, Uri.UriSchemeHttps, Uri.UriSchemeFile})
{
Foo foo = new Foo() { A = a, B = a, C = a, D = scheme};
if(foo.Bar()) trues++;
}
Assert.That(trues, Is.EqualTo(3));
}

Terse? Check. Exhaustive? Check. Sufficiently different? Check. It seems unlikely that, if I refactor this code six weeks from now, I'd make a mental mistake on the implementation that would easily transfer to the test as well.

As they say in math, the proof is by counting.

Sunday, November 16, 2008

Indiscipline kills Agile

That Jim Shore is doing it again. Here's another post I put on his blog.

Agile isn't for everyone. Is that heresy? I turn now, equally heretically, to a sports analogy.

In baseball, there's a belief that an underachieving team can sometimes be improved by bringing in a manager who's not just a different face but a different style. If the previous regime was easygoing with the players, the new one needs to be a disciplinarian, and vice versa. In clubs that are perpetually bad, there's often a perceptible oscillation over time between the two poles.

The interesting thing is, the strategy is thought to work in the short term. Intense managers raise the team's intensity, yielding better results; looser managers increase the team's--I dunno--zen, also improving results. For example: the Tampa Bay Rays got to the World Series this year with a prototypical "player's coach" (Joe Maddon) who replace perhaps THE prototypical disciplinarian, Lou Piniella.

Over time (the conventional wisdom goes), players adjust, cultures change, the results ebb, and eventually the team is ripe for a new revolution.

To return, finally, to software: hidebound developer teams can benefit from Agile. They discover new things about themselves and their business by going iterative and tightening feedback cycles. If they are sufficiently self-aware to capture their discoveries, their engineers can probably engineer an improvement here and there.

However, software developers that can't sustain their own discipline amidst some of Agile's freedoms--i.e., developers who are not especially professionalized or engineerish--are not going to thrive with Agile. Not long term. Indiscipline kills Agile.

Thursday, November 13, 2008

C# primers

So, for reasons bureaucratic related to my master's degree, I've been taking a .NET class. I don't especially recommend taking courses in things you already largely know--especially if long-distance. If you're going to disagree with someone on a philosophical or stylistic point of code, you don't want to do so by email.

Anyway, one of my classmates asked the class,

> What outside reference materials, books, sites, personal advice, etc
> would you recommend for someone with a subpar knowledge of C#/.net in
> regards to this class?

My reply was lengthy enough that it now becomes its own post. It's old hat but I wanted a copy in the archive.

* * *

One of MS's top developer blogs is Scott Guthrie's. He was away over the summer for paternity leave but is gearing up again lately. He writes lots of "how to" pieces and overview pieces about new releases or forthcoming products. He's very senior, so if scottgu's not writing about it, he's probably the boss of someone who is and thus will have a link to it. The comments on his blogs are often useful, too. Extensive archives to search.

Another top blogger is Scott Hanselman, who went to MS about a year ago after being CTO at a firm that got bought. His job now is more or less to evangelize MS developer technologies to the community and advocate for developer issues within MS. His writing is good, and the
comments on his blog entries are usually very smart.

I mostly read the articles but he has a podcast and frequent screencasts, too. Be careful, though: Hanselman is co-author of "Professional ASP.NET in C# and VB", a book that, in my opinion, is a waste of time, money, and trees. It basically repeats what is obvious from the APIs and docs: no useful advice or insight. The code samples are trivial. If I were Hanselman, I'd take my name off the book.

I agree with others' assessments of Jeffrey Richter's main .NET book. Really good but dense. He has a blog but doesn't post much.

Richter is also a contributor to "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries", which is very good at filling a certain niche. The book is almost like a printout of a blog: there are two main authors but several other contributors (such as Richter) whose comments are in sidebars throughout the text. This book won't help you learn how to write your first C# program; it's mostly an explanation of various decisions that
were made by the people who designed the .NET Framework, and recommendations on what "programming in a .NET style" might mean. It also provides anecdotally interesting insight into the amount and styles of usability testing that MS does with its developer tools. (These guys are all very smart, but they realize that they're writing tools for an entire industry of developers, not just the world's elite. When I get worked up about MS doing something stupid, it's nice to be reminded that sometimes MS omits effective solutions not out of raw incompetence but because they want to be all things to all people.) Anyway, if you're someone whose learning/retention is
improved by understanding why* the framework is a certain way, or if you just enjoy deeper understanding, this book can be very helpful.

Joe Duffy is another very smart guy who writes well, and he's written a book for beginners: Professional .NET Framework 2.0. I've only had it for a few weeks but I've liked all the parts I've read. His blog is very technical. He works at MS on parallel programming extensions
to the .NET framework.

If you're going to work professionally on ASP.NET, read Stephen Walther. His "ASP.NET 3.5 Unleased" is my favorite ASP.NET book so far, and his blog has been a steady stream lately of
interesting experiments with the forthcoming ASP MVC framework.

Eric Lippert works on .NET language and developer stuff at MS. Very smart and reflective about how tools and languages relate to good design. He's for professional developers; not recommended fo people who haven't programmed much or who don't plan to. Really wonderful
stuff, though.