DaedTech

Stories about Software

By

Poor Man’s Automoq in .NET 4

So, the other day I mentioned to a coworker that I was working on a tool that would wrap moq and provide expressive test double names. I then mentioned the tool AutoMoq, and he showed me something that he was doing. It’s very simple:

 [TestClass]
    public class FeatureResetServiceTest
    {
        #region Builders

        private static FeatureResetService BuildTarget(IFeatureLocator locator = null)
        {
            var myLocator = locator ?? new Mock().Object;
            return new FeatureResetService(myLocator);
        }

        /// If you're going out of your way to pass null instead of empty, something is wrong
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void ResetToDefault_Throws_NullArgumentException()
        {
            var myService = BuildTarget();

            ExtendedAssert.Throws(() => myService.ResetFeaturesToDefault(null));
        }

The concept here is very simple. If you’re using a dependency injection scheme (manual or IoC container), your classes may evolve to need additional dependencies, which sucks if you’re declaring them inline in every method. This means that you need to engage in a flurry of find/replace, which is a deterrent from changing your constructor signature, even when that’s the best way to go.

This solution, while not perfect, definitely eliminates that in a lot of cases. The BuildTarget() method takes an optional parameter (hence the requirement for .NET 4) for each constructor parameter, and if said parameter is not supplied, it creates a simple mock.

There are some obvious shortcomings — if you remove a reference from your constructor, you still have to go through all of your tests and remove the extra setup code for that dependency, for instance, but this is still much better than the old fashioned way.

I’ve now adopted this practice where suitable and am finding that I like it.

(ExtendedAssert is a utility that I wrote to address what I consider shortcomings in MSTest).

By

GitHub and Easy Moq

GitHub

Not too long ago, I signed up for GitHub. I had always used subversion for source control when given the choice, but the distributed nature of Git appealed to me, particularly since I often do work on a variety of different machines. GitHub itself appealed to me because it seemed like the perfect venue for reusable code that I’ve created over the years and tend to bring with me from project to project. Here is a unique idea – sites like Sourceforge want to store code by basic unit of “project” whereas GitHub wants code for the sake of code. This code need not be finished or polished or distributed on any kind of release schedule. That is perfect for a lot of my portable code.

One thing that I noticed about GitHub upon arrival was the lack of .NET work there, compared with other technologies. There is some, but not a lot. Presumably this is because sites like Code Project already offer this kind of thing to the .NET community. After reading about Phil Haack leaving Microsoft to be GitHub’s Windows Ambassador, I decided that this was a good indicator that GitHub was serious about growing that portion of its user base, and that I’d start porting some of my code there.

Another thing that I’d read about git before trying it out was that it has a steep learning curve. To back my general impression of this as a widely held opinion, I did a quick google search and ran across this post.

Given the steep learning curve many (including myself) experience with Git, I think I can safely assume that many other people have this sort of problem with it.

So, there’s at least one person who has experienced a steep learning curve and at least one other person with the same general sense as me about many finding the learning curve steep.

I must say, I haven’t found this to be the case. Perhaps it’s my comfort with Linux command line interaction or perhaps it’s my years of experience using and administering SVN, but whatever it is, I read the GitHub primer and was up and running. And, I don’t mean that I was up and running with commits to my local repository, but I was up and running committing, pushing to GitHub, and pulling that source to a second computer where I was able to build and run it. Granted, I haven’t forked/branched/merged yet, but after a day of using it, I was at the point where the source control system retreated into the mental background where it belongs, serving as a tool I’m taking for granted rather than a mental obstacle (by contrast, even with a couple of years of experience with Rational Clear Case, I still have to set aside extra development time to get it to do what I want, but I digress, since I don’t believe that’s a simple case of needling to mount a learning curve). So, after a day, I’m developing according to my schedule and running a commit when I feel like taking a break and/or a push when I want to update GitHub’s version. I’m sure I’ll be hitting google or StackOverflow here and there, but so far so good.

Easy Moq

So, what am I doing with my first GitHub project? It’s something that I’m calling Easy Moq. I use Moq as my main mocking framework, as I’ve mentioned before, and I’ve had enough time with it to find that I have a certain set of standard setup patterns. Over the course of time, I’ve refined these to the point where they become oft-repeated boilerplate, and I’ve abstracted this boilerplate into various methods and other utilities that I use. Up until now, this has varied a bit as I go, and I email it to myself or upload the classes to my web server or something like that.

But, I’m tired of this hodgepodge approach, and this seems like the perfect use case for trying out GitHub. So, I’m looking over my various projects and my use of Moq and creating my own personal Easy Moq library. If others find it useful, so much the better – please feel free to keep up with it, fork it, add to it, etc.

At the time of writing, I have three goals in mind (these may change as I TDD my way through and start eating my own dog food on real projects). In no particular order:

  • Create Mock inheritors that default to having specific test double behavior – dummies, stubs, spies, etc.
  • Be able to create and use my doubles with the semantics Dummy() or new Spy() rather than new Mock().Object
  • Be able to generate an actual class under test with all dependencies mocked as Dummy, Stub, Spy, etc.

For the first goal, I’ll refer to Niraj’s take on the taxonomy of test doubles. I haven’t yet decided exactly how I’ll define the particular doubles, but the ideas in that post are good background for my aims. Up to this point, if I want to create one of these things with Moq, I instantiate the double in the same fashion and my setup differs. I’d like to make the declaration semantics more expressive — declaring a DummyMock is more expressive as to your intent with the test than creating a Mock and doing nothing else to set it up. For more complicated doubles, this also has the pleasant effect of eliminating manual boilerplate like “SetupAllProperties()”.

For the second goal, this is probably lowest priority, but I’d like to be able to think of the doubles as entities unto themselves rather than wrappers that contain the entity that I want. It may seem like a small thing, but I’m a bit of a stickler (fanatic at times) for clear, declarative semantics.

For the third part, it’d be nice to add a dependency to a class and not have to go back and slaughter test classes with “Find and Replace” to change all calls to new Foo(bar) to new Foo(bar, baz), along with Baz’s setup overhead, particularly when those old tests, by definition, do not care about Baz. I realize that there is already a tool designed to do this (called AutoMoq), but it appears to be for use specifically with Unity. And besides, this is more about me organizing and standardizing my own existing code as well as getting to know GitHub.

So, that’s my first pet project contribution to Git Hub. If anyone gets any mileage out of it, Cheers. 🙂

By

More and More With TDD

TDD Revisited Again

Previously I blogged about TDD with some reservations and then later as I re-introduced myself to the pure practice as I had learned it some many moons ago. For the last month, I have been plowing ahead methodically with this experiment, and I thought I’d revisit some of my thoughts from earlier.

Reinforced Pros

First off, the pros still stand in my mind, but I’d like to add one enormous additional pro, which is that you get code right the first time. I mean, really, really right. Right as in coding for a few days without ever running the application you’re working on, and then firing it up and having it work flawlessly the first time you run it.

And, I’m not talking about plodding along with some tiny development exercise like scoring a bowling game. I mean, I recently wrote an application that would parse a text file, format and filter records, and insert the result in a database. Using a combination of Moq for my internal seams and Moles for external concerns (file and database), I exhaustively tested everything I wanted to do as I went, following the red-green-refactor process to the letter. After some hours here and there of working on this (shaking off my TDD rust being part of the time spent), I finally hit F5 and it worked. My files were parsed, my databases updated, my log file written accurately. This wasn’t a fluke, either, because I repeated the process when I wrote another utility that piggy-backed on this first one to send out emails with the results. I wrote an API for setting up custom mail messages, dependency injection for different mail clients, and wireup code for integrating. Never once did I step through the debugger trying to figure out what network credential I had missed or which message was missing which formatting. I put the finishing touches on it with my tests passing and code coverage at 100%, hit F5 and had a well-formed, perfect message in my inbox before I even looked away from my screen, expecting to be kicked into the debugger. That is a remarkable pro.

I mean, think of that. If you’re a skeptic about TDD or unit testing in general, you’ve probably never experienced anything like that. If you have, that’s amazing. I don’t know anyone that doesn’t develop in this fashion that has this happen. I mean, people who are really sharp get it mostly right, and sometimes just have to tweak some things here and maybe step through a few functions a few times, but without ever running the code? I’m not buying it. And even if they did, I come out way ahead this way. A non-tested code base is going to be harder to modify. I have hundreds of passing tests now that will tell me if subsequent changes break anything.

Cons Mitigated

So, I’d like to take a look back at the cons, and revisit my opinions after a month of doggedly sticking with TDD:

  1. Larger design concepts do not seem to be emergent the way they might be in a more shoot from the hip approach — figuring out when you need a broader pattern seems to require stepping out of the TDD frame of mind
  2. There is no specific element of the process that naturally pushes me away from the happy path — you wind up creating tests for exceptional/edge cases the way you would without TDD (I guess that’s not strictly a con, just a lack of pro).
  3. It becomes easier to keep plodding away at an ultimately unworkable design – it’s already hard to throw work you’ve put in away, and now you’re generating twice as much code.
  4. Correcting tests that have been altered by changing requirements is magnified since your first code often turns out not to be correct — I find myself refactoring tests as often as code in the early going.
  5. Has the potential to slow development when you’re new to the process — I suppose one might look at this as a time investment to be paid off when you get more fluid and used to doing it.

Regarding (1) and (3), this seems not to be the issue that I thought it would be. I had grown used to a fast flurry trying out a class concept, and realizing what refactorings needed to be done on the fly, causing it to morph very quickly. I had gotten very proficient at ongoing refactoring without the red-green preceding it, so, in this way, I was able to ‘fake’ the TDD benefit. However, I realized a paradoxical benefit – TDD forced me to slow down at the beginning. But rather than really slowing me down on the whole, it just made me toss out fewer designs. I still refactor with it, but it isn’t necessary as often. The TDD forces me to say “what do I want out of this method/class” rather than to say “what should this do and how should it relate to others”. The difference is subtle, but interesting. The TDD way, I’m looking at each method as an atom and each class as a unit, whereas the other way I’m putting the cart a bit before the horse and looking at larger interactions. I thought this was a benefit, but it resulted in more scratch out work during prototyping than was really necessary. I was concerned that TDD would force me to throw away more code because I’d be tossing tests as well as code, but in reality, I just toss less code.

Regarding (2), this has sorted itself out as well. TDD does tend to lead you down the happy path as you’re using it to incrementally correct and refine a method’s behavior, but there’s nothing that stops you from tossing in along with that a thought of “how do I want this to handle an exception in a method that it calls?” This, I’m finding is a discipline and one that I luckily brought with me from testing after or testing during. It is not incompatible with TDD, though TDD does help me not get carried away focusing on corner cases (which can be ferreted out with tools like Pex or smoke tests later).

Regarding (4), this has also proved not to be too much of an issue, though I have seen a touch of this. But, the issue really comes in when it turns out that I don’t need some class, rather than a swath of tests. TDD helps my classes be better thought out and more decoupled, and so, I’ve found that when I’m cutting things, it tends to be surgical rather than a bloodbath.

Regarding (5), this is true, but I’ve fought through the burn. However, there is another thing that goes on here that I’d point out to people considering a foray into TDD. Imagine the scenario that I described above, and that you’re the non-TDD hare and I’m the TDD tortoise. I’m plodding along, red-green-refactoring my parser when you’ve finished yours. You’re probably running it on an actual file and making changes while I’m still working methodically. By the time I’m finished with my parser and doing the formatting, you’ve got your formatting finished and are working on the database. By the time I get to the database, you’ve already got things working 90%, and are busy stepping through the debugger and cursing the OleDbCommand API.

But, that’s where things start to get interesting. Often times, in my experience, this is the part of development that has you at the office late, pulling your hair out. The application is 90% done at 4:00, and you’re happy because you just need to make a minor tweak. Suddenly, it’s 8:00 and you’re hungry and cranky and cursing because no one is left in the office. That’s a brutal 4 hours specifically because you imagined in to be 20 minutes at 4:00. But, let’s say you finally get it and go home. I’ll finish an hour or so after you and you win the first leg of the race, though I’ll do it without a lot of fanfare and frustration, since I’m never hitting snags like that with my methodical process.

But now, an interesting thing happens. We send our versions out for Beta testing, and the issues start coming back. You have your own knowledge of your code to go on as you fix defects and perform minor refactorings, while I have 100% code coverage. I quickly find issues, fix them, see all green and am ready to deliver. You quickly find issues, fix them, and then run the application a bunch of times in different scenarios trying to ferret out anything that you could possibly have broken. I’m gaining on you fast. And, I’m going to keep gaining on you as the software grows. If Beta lasts more than a short time, I’ll win. If there are multiple versions of the software, I’ll win easily. At some point, I’ll lap you as you spend way more time repairing defects you’re introducing with each change than fixing the one you set out to fix. The time investment isn’t new TDD versus seasoned TDD. It’s a little time up front for a lot of time later.

Eating Crow

I had tried TDD in the past, but I think I wasn’t ready or doing it right. Now, I’m convinced that it’s definitely the path for me. Somewhere, I saw a presentation recently that mentioned sort of an arc of unit testing. People tend to start out with test-last, and then move to test during, then test-first and then test-driven, according to this presentation (I wish I remember whose it was so I could link and give credit). I think my previous issue was that I had short circuited the arc and was trying to assimilate too much all at once. If you’re a developer trying to convince others of the merits of unit tests, TDD might be a lot to spring on them up front.

I was thinking about why that might be, and something occurred to me from way back in my undergrad days. The first thing we all tend to do is want to write all of the code at once. Since we’re given small assignments to start, this works. It’s hard to turn bubble sort into a lot of different feedback passes — you write the whole thing and then debug the ‘finished’ product. As classes get harder, you try to keep this dream alive, but at some point you have the “aha” moment that you need to get shorter feedback cycles to prevent assembling the whole thing and getting it all wrong. So, you do this by making your code modular and developing components. That carries you through into the early part of your career, and you have a natural tendency to want to get code up and running as quickly as possible for feedback. Then, TDD comes along and changes the nature of the feedback altogether. You’re no longer getting the feedback from your running application, but from your unit tests of small components. So, if you’re building a house app, you don’t start with the House class and run it. Instead, you start with a “Bedroom” class, or even a “Bed” class. While your counterparts are adding rooms to their running houses, you’re getting your “Bed” class working flawlessly. And, while they’re cobbling furniture into their rooms, you’re assembling rooms from their furniture. When they’re debugging, you’re finally ready for your first “F5” feedback. But, you haven’t come full circle and turned into that freshman with bubble sort – you’ve gotten real feedback every 30 seconds or so. You’ve gotten so much feedback, that there’s little anticipation with the first real run of the component you’re working on for that week. You’re pretty sure it’s going to work and you’re going to be heading home at 5:00.

So, yeah, I guess I’m a TDD convert, and will eat some crow for my earlier posts.

By

In Search of the Perfect Code Review

Code Reviews

One thing that I tend to contemplate from time to time and have yet to post about is the idea of code reviews and what constitutes a good one. I’ve worked on projects where there was no code review process, a half-hearted review process, an annoying or counter-productive code review process, and a well organized and efficient code review process. It’s really run the gamut. So, I’d like to put pen to paper (finger to keyboard) and flesh out my ideas for what an optimal code review would entail. Before I can do that, however, I think I need to define some terms up front, and then identify some things about code reviews that I view as pitfalls, counter-productive activities or non-useful activities.

What is a Code Review?

According to the wikipedia entry for a code review, there are three basic types: the formal review, pair programming, and the lightweight review. I’ll add a fourth to this for the sake of pure definition, and call that the “automated review”. The automated review would be use of one or more static analysis tools like FX Cop or Style Cop (the wiki article includes someone using tools like this on the code of another developer, but I’m talking strictly automated steps like “you fail the build if you generate Style Cop warnings”). Pair programming is self explanatory for anyone familiar with the concept. Lightweight reviews are more relaxed and informal in the sense that they tend to be asynchronous and probably more of a courtesy. An example of this is where you email someone a source code file and ask for a sanity check.

The formal review is the one with which people are probably most familiar if code review is officially part of the SDLC on the project. This is a review where the developer sits in a room with one or more other people and presents written code. The reviewers go through in detail, looking for potential bugs, mistakes, failures to comply with convention, opportunities for improvement, design issues, etc. In a nutshell, copy-editing of code while the author is present.

What I’ll be discussing here mainly pertains to the formal review.

What I Don’t Like

Here are some things that I think ought to be avoided in a code review process.

1. Failing to have code reviews

This is important in the same way that QC is important. We’re all human and we could all use fresh eyes and sanity checks.

2. Procedural Code Review: The Nitpick

“You made that camel case instead of Pascal case.” “You could be dereferencing null there.” In general, anything that a static analysis tool could tell someone a lot faster and more accurately than a room full of people inspecting code manually. Suresh addresses this idea in a blog post:

I happen to see only “superficial” reviews happening. By superficial, I mean the types where you get review comments like, “You know the documentation for that method doesn’t have the version number”, or “this variable is unused”, etc.

“Superficial” is an excellent description as these things are generally trivial to identify and correct. It is not an effective use of the time of the developer or reviewers anymore than turning off spell check and doing it manually is an effective use of authors’ and editors’ time. There are tools for this — use them!

3. Paranoid Code Review

This is what happens when reviewer(s) go into the review with the notion that the only thing standing between a functioning application and disaster is their keen eye for the mistakes of others. This leads to a drawn out activity in which reviewers try to identify any possible, conceivable mistake that might exist in the code. It’s often easily identified by reviewers scrunching their noses, concentrating heavily, pointing, or tracing code through control flow statements with their fingers on the big screen while the developer twiddles his thumbs.

Again, there’s a tool for this. It’s called the unit test. It’s not flawless and it assumes a decent amount of coverage and competence from the unit tests, but if executed properly, the unit tests will express and prove corner case behavior far better than people on too little sleep staring at a screen and trying to step through the call stack mentally. This mental execution is probably the least reliable possible way of examining code.

4. Pure Gatekeeper Code Review

This is less of an individual property of code review, but more a property of the review process. It’s where you have a person or committee in the department that acts as the Caesar of code, giving thumbs up or thumbs down to anything that anyone submits. Don’t get me wrong — the aim of this makes sense. You want somebody that’s doing a sanity check on the code and someone who more or less has his or her finger on the pulse of everything that’s happening.

The issue that occurs here is a subtle one that results from having the same person or people reviewing all code. Specifically, those people become the gatekeepers and the submitters become less concerned about writing good code and innovating and more principally concerned with figuring out how to please the reviewer(s). Now, if the reviewers are sharp and savvy, this may not be a big deal, though discouraging new ideas and personal growth is always going to have some negative impact. However, if the reviewers are not as sophisticated, this is downright problematic.

This is relatively easily addressed by rotating who performs code reviews or else distinguishing between “suggestion” and “order”. I’ve participated in review processes where both of these mitigating actions were applied, and it’s a help.

5. Tyrant Gatekeeper

In the previous example, I mentioned a hypothetical gatekeeper reviewer or committee with ultimate authority, and this is a subset of that. In this case, the reviewer(s) have ultimate yes/no authority and are very heavy (and possibly even combative or derisive) with the “no” option. Where the previous example might stifle innovation or developer growth, this creates bottlenecks. Not only is it hard to get things approved, but developers will naturally start asking the reviewer(s) what to do at every turn rather than attempting to think for themselves.

In essence, this creates a state of learned helplessness. Developers are more concerned with avoiding negative feedback at the code reviews than learning, doing a good job, or becoming able to make good decisions based on their own experience. As a result, the developers don’t really make any decisions and ask the reviewer(s) what to do at every step, waiting until they have time, if necessary. The review(s) become a bottleneck in the process.

I have not personally witnessed or been subject to this form of code reviews, but I have heard of such a thing and it isn’t difficult to imagine this happening.

6. Discussion Drift

This occurs during a code review when a discussion of the specific implementation gets sidetracked by a more general discussion of the way things ought to be. Perhaps the code is instantiating an object in the constructor, and a reviewer recommends using dependency injection instead. From here, the participants in the review being to discuss how nice it would be if the architecture relied on a IOC framework instead of whatever it is at the moment.

That’s both a valid and an interesting discussion, but it has nothing to do with some developer checking in implementation code within the framework of the existing architecture. Discussions can have merit and still not be germane to the task at hand.

7. Religious Wars

This occurs during a code review in the following context:

Reviewer 1: You didn’t put curly brackets around that one liner following your if statement. Please add them.
Reviewer 2: No, don’t do that. I hate that.
Reviewer 1: No, it’s better. That way if someone changes the code and adds another statement…. etc.
Reviewer 2: Studies have shown….
Review-ee: Uh, guys….

And so on and so forth. Code reviews can easily devolve into this sort of thing over purely or nearly-purely subjective matters. People get very entrenched about their own subjective preferences and feel the need to defend them. We see this from political affiliation to rooting for sports teams. Subjective matters of preference in code are no different. Neither side is likely to convince the other during the scope of the review, but what is quite likely, and probably certain, is that time will be wasted.

If matters like that are part of the coding standards policy on the project, than it’s an open and shut case. If they’re not, they’re better left alone.

So, How Does a Good Code Review Go?

Having explained what I don’t like in a code review, I’ve provided some context for what I do find helpful. I’m going to outline a procedure that is simply an idea. This idea is subject to suggestions for improvement by others, and ongoing refinement from me. Also, this idea is geared toward the gatekeeper scenario. I may make another post on what I perceive to be the most effective method for voluntary(ish), peer-conducted code reviews where the reviewer(s) are not approval gate-keepers.

  1. Developer finishes the pre-defined unit of code and is ready to have it reviewed for promote/commit.
  2. Developer runs static analysis tools (e.g. StyleCop, FXCop, Code Contracts, NDepend, etc) with configured rule-sets, correcting any errors they uncover.
  3. Once no static-check violations are present, developer notifies reviewer(s) of desire for code review.
  4. Reviewers run static analysis tools asynchronously and reject the request if any rules are violated.
  5. Reviewers examine the code for obvious mistakes not caught by static analysis and/or developer unit tests, and write unit tests that expose the deficiency. (Alternatively, they can run something like Pex)
  6. Developer makes reviewer unit tests pass or else convinces reviewer(s) why they don’t need to.
  7. With all unit tests passing, and all parties familiar with the code, a review meeting is setup (meeting can be skipped for smaller/less crucial code deliveries).
  8. Meeting proceeds as follows:
    1. A mediator who is neither developer nor reviewer is asked to attend to keep the meeting focused and on track.
    2. Reviewers point out something praiseworthy about the submitted code (cheesy, perhaps, but important for starting off in the spirit of cooperation)
    3. Reviewers examine code for redundancy (is anything copy/pasted, defined in many places, etc)
    4. Reviewers examine the code for usable API, perhaps by implementing classes in a sandbox, to highlight shortcomings, unintuitive interactions, weird couplings, etc
    5. Reviewers check for architectural consistency — does the class implement a base class that it should, duplicate the function of some other class in the suite, seem to be in the wrong location, etc.
    6. Reviewers perform a dependency analysis — what new dependencies does this introduce? Cyclical, global, temporal, etc. Are these dependencies acceptable?
    7. Reviewers analyze for code smells and anti-patterns.
    8. Reviewers compile a list of suggested changes for the developer.
    9. Meeting adjourned.
  9. Developer makes suggested changes that he agrees with.
  10. Developer appeals changes with which he doesn’t agree. This could involve the reviewer(s) providing “proof”, for example if they say that the developer shouldn’t do something because of X negative consequence, they should demonstrate that consequence somehow. This appeal could be resolved via proof/demonstration or it could go to a third party where the developers and reviewers each state their cases.
  11. Any suggested changes and the results of any appeals are then promoted/committed.

This process naturally addresses (1) and (2) of the things that I don’t like in that you’re having a code review and getting the procedural, easy stuff out of the way offline, prior to meeting. (3) is made more difficult by the fact that the reviewer(s) are given the opportunity to write unit tests that expose the badness about which they might be paranoid. (4) and (5) are addressed by the appeal process and the general concept that changes are suggestions rather than decrees. (6) and (7) are addressed by the mediator who has no skin in the game and will probably have a natural tendency to want to keep things short and sweet.

One drawback I can see to what I’m proposing here is that you could potentially undercut the authority of the reviewer if the person doing the reviews is, say, the most senior or high ranking person. Perhaps people would want that role a bit less if they could be officially second guessed by anyone. However, I think that creates a nice environment where good ideas are valued above all else. If I were in that role myself, I’d welcome the challenge of having to demonstrate/prove ideas that I think are good ones before telling others to adopt them. In the long run, being steered toward that kind of rigor makes you better at your craft. I also think that it might be something of a non-issue, given that people who wind up in these types of leadership and senior roles are often there based on the merit of their work and on successfully “proving” things over and over throughout the course of a career.

By

Adventures in Pure Test-Driven Development

In a previous post some time back, I had expressed some skepticism about TDD as a design practice. I talked about test-driven development and its relationship with prototyping and the “make one to throw away” concept.

Since I’m not one ever to believe that I’ve arrived at the optimal solution, I’m doing another round of pure TDD to see if I can ‘refactor’ my process a little and find improvements in efficiency and/or code quality. As I’m doing this, I’m finding that I’m a little more proficient in the TDD concept than I was in previous attempts, but I’m still not convinced that TDD is a golden hammer appropriate for all solutions, all the time.

I’m going to make a list of pros and cons here as I work, both for myself and for posterity. Once I’ve compiled the list, I’ll wait a little bit, reflect, and write a conclusion on it (so yes, the part at the bottom will be written two or days removed from what you’re reading right now).

Pros

  • Using mocking frameworks (Moles and Moq), maintaining 100% code coverage is effortless
  • Tests tend to be more meaningful for your use cases than retrofitted ones.
  • Better gaurding against regressions
  • TDD is better even than I remember at forcing me to think through various scenarios of method control flow and object state. I’m having far fewer cases of, “oh, I didn’t consider scenario X!”
  • Cuts down on potential for dead code. I’m not doing things like declaring a setter because I’m already declaring a getter and I might as well. The rules of the process don’t let you code that until you have a test for it.
  • Tends to help generate useful and well-conceived abstract utility classes (such as a no-exception wrapper for string splitting that I wrote) that can be reused elsewhere.

Cons

  • Larger design concepts do not seem to be emergent the way they might be in a more shoot-from-the-hip approach. Figuring out when you need a broader pattern seems to require stepping out of the TDD frame of mind
  • There is no specific element of the process that naturally pushes me away from the happy path. You wind up creating tests for exceptional/edge cases the way you would without TDD (I guess that’s not strictly a con, just a lack of pro).
  • It becomes easier to keep plodding away at an ultimately unworkable design. It’s already hard to throw work you’ve put in away, and now you’re generating twice as much code.
  • Correcting tests that have been altered by changing requirements is magnified since your first code often turns out not to be correct. I find myself refactoring tests as often as code in the early going.
  • It has the potential to slow development when you’re new to the process. I suppose one might look at this as a time investment to be paid off when you get more fluid and used to doing it.

Conclusions

I like aspects of this process, and I like it enough that I’m going to keep making stabs at it to find exactly how it works best for me. In my earlier post, I mentioned that I’d do prototyping without tests and then do TDD in earnest when I started writing my “for-real” code. I think I’m going to try to phase it in increasingly to my prototyping as well.

My biggest issue is my perception that TDD removes more emergent macroscopic principles of design. There is no straightforward refactoring I’m aware of that leads to a MVVM pattern of development or to using something like a flyweight. So it’s going to be up to me and my refinement of my own process to reconcile Uncle Bob’s three laws of TDD with sophisticated design concepts.

Of course, I’d be interested to hear, in comments, anyone’s take on how to reconcile these two concepts. Perhaps I just haven’t been at it long enough to see a test that I can write that demands a refactoring to a more complex pattern. Or, perhaps I’ve become so used to recognizing good fits for patterns ahead of time that it’s no longer part of a refactoring for me, but the original ‘factoring’. In that case, I suppose I’d have to retool my methodology for arriving at design patterns.

Regardless, though, it should be an interesting endeavor. I like to shake up my way of doing things as often as possible to look for opportunities for improvement.