Showing posts with label Test-Driven Development. Show all posts
Showing posts with label Test-Driven Development. Show all posts

Thursday, April 10, 2014

Another Generally Positive Review for Test-Driven Database Development

I would be a fool to think that my first book would receive only positive feedback.  This review, I think, is excellent.  I like it for three reasons:

  1. My own self-interest
  2. The reviewer clearly understood what he was reviewing
  3. It contains actionable criticisms that will benefit a 2nd edition, if there is one

Vanity of the Database Authors

Mr. Carlson's review is, I think, generally positive.  This means that it will cause more people to buy my book, which is nice.  It also means that it fans the flames of my own vanity, which is really nice.  Most importantly, though, it means that more people are likely to actually read my book which, in turn, means more people are likely to take a sane approach to database development.

Deep Understanding

The author of the review in question clearly took the time to read, understand, and evaluate the concepts in my book.  I think a lot of people who buy books do this but, for some reason, only about half of people who write reviews appear to do it.

Mr. Carlson didn't necessarily understand all of my motivations or what I know about outside the scope of Test-Driven Database Development: Unlocking Agility but... hey... who's fault is that?  Right?

Actionable Criticisms

Not everything Mr. Carlson has to say is positive but those bits which are negative are highly actionable.  He says I give the appearance of not understanding normalization.  I do understand how to do it and why people do it in addition to why it actually should be done.  However, looking back, I can see how someone might get the impression I don't if they were basing their opinion solely on my book.  In the 2nd edition, I will remedy that by adding material that addresses those kinds of concepts.

Likewise, he makes the point that I don't address object-relational-mapping at all.  He rightly guessed that it is because ORM runs contrary the message I send - databases as instances of classes with tightly-controlled and rigorously-tested sets of exposed behaviors.  Nevertheless, his argument is that ORM is a very popular concept in designing interactions with databases is true.  If I ever get to do a second edition, which I happily think I might, and ORM is still popular, which I sadly believe it may be, then I will address it at that time.

I may address those concepts sooner, too, in the form of blog entries or articles.

Thursday, December 19, 2013

Next Step: Test-Driven Architecture

Recently, I published an article on InformIT about how we should focus our software-architectural efforts.  I've been blogging about it a little bit since.

Like everything in the software development industry, Test-Driven Development can make architecture better.  So Mike Brown and I have set out to document our ideas on that subject.  Originally, that was going to be a single article on InformIT.

That's not going to cut it.  As one might have guessed (I didn't but, now, I can imagine someone else might), the topic is too rich to fit into a single 3,000 word article.  So, with luck, it will turn into a series of articles written by my friend and myself (and possibly others) that help this industry take another step toward being truly test-driven.

Friday, December 06, 2013

I'll Be Speaking At DAMA SoCal in February

This is just a quick announcement that I will be giving the same talk I mentioned earlier again at DAMA SoCal on February 24th, 2014.

Click here for more details.

Monday, September 30, 2013

When to Create a SetUp Method

I recall from back when the Earth was cooling and I was spending a of time in Seattle (read: "living there") that there was a lot of advice flying around about the forces surrounding designing individual tests.

A particular point of interest was when to factor stuff out into a setup method.  One camp said "never factor something into a setup method until it's needed by more than one test."  Another camp said "factor stuff out into a setup method as soon as you reasonably can."

At the time, I didn't really care so I just went along with my friends.  Now I think I do care.  I've noticed that factoring certain things out into a set up method improves the readability of a test, even when there is only one.

That, to me, settles the "when" question by obviating it and uncovering a much more interesting question: what should you factor out into a set-up method?

I've noticed that, for me, there are two kinds of set-up needed: context stuff and test-specific stuff.  The context stuff is obvious.  The test-specific stuff is interesting.

For a human, there is no information in a context-setting line of code.  It is still informative to a compiler, which is why you need it, but a person trying to understand an API would easily fill in the blanks if he never saw that line of code.  The only real effect such a line of code has on a human-reader is to make a test less-readable by adding noise.  An example of context-setting set-up is instantiating the class being tested.

By contrast, the test-specific line of code is very interesting to a human.  It explains what is happening or why it is happening.  Examples of test-specific code would be invoking a method on the class under test or asserting on the results of such an invocation.

Let's look at two examples.

Here's a test with all the set-up in the test method to prevent a preemptive refactor:

[Test]
public void UnderOrdinaryCircumstancesPassesThroughToCore()
{
  var core = new Mock<PathFinder>();
  var specialEdge = new Mock<PathFinderEdge>();
  var specialEdgeFinder =
    SpecialFirstEdgePathFinder.GetInstance(core.Object, specialEdge.Object);
  var origin = Design.Element.GetInstance(null, null, null);
  var keyPath = new[] { Any.Symbol, Any.Symbol };
  var elementPath = new[] {
    Design.Element.GetInstance(null, null, null),
    Design.Element.GetInstance(null, null, null), };
  core.Setup(c => c.Navigate(origin, keyPath)).Returns(elementPath);

  var actual = specialEdgeFinder.Navigate(origin, keyPath).ToArray();

  Assert.That(actual, Is.EqualTo(elementPath));
}

Here's a test with all the set-up in the test method to prevent a preemptive refactor:

[Test]
public void UnderOrdinaryCircumstancesPassesThroughToCore()
{
  var keyPath = new[] { Any.Symbol, Any.Symbol };
  var elementPath = new[] {
    Design.Element.GetInstance(null, null, null),
    Design.Element.GetInstance(null, null, null), };
  core.Setup(c => c.Navigate(origin, keyPath)).Returns(elementPath);

  var actual = specialEdgeFinder.Navigate(origin, keyPath).ToArray();

  Assert.That(actual, Is.EqualTo(elementPath));
}

The two tests specify the exact same thing.  The only difference, in my mind, is how they read.  To me, the latter is a lot more clear than the former.

The process that works for me is as follows:
  1. Write the test with no set-up to discover what the test should be
  2. Make the test pass
  3. In the refactoring phase, move all the "clutter" to a context-setting setup method
I'm open to other arguments but this is working for me now.