Question of the Day

The difficulty and pain of TDD adoption is a hot topic over the last couple days.  I’m working on an article on testability and design this morning.  In writing the summary I caught myself saying that there are some exceptional cases where I do unnatural things to promote testability, but that most of testability design is really a stronger focus on the classical OO design qualities of cohesion, coupling, and separation of concerns.  Here’s the question of the day though, most of those “exceptional cases” and “unnatural things” are directly related to working around C#’s static type system (marking things as virtual, exposing members as public or doing the InternalsVisibleTo trick, etc.).  Here’s the question of the day:

Will you adopt a dynamic language like IronRuby or try out “side effect free” programming with functional languages like F# just because of the lower friction in TDD from these languages?  There’s an obvious cost and risk in switching over, not to mention less tooling, but at what point do we say that there’s more potential for higher productivity in these other languages for TDD practitioners and make the jump.  My team considered Ruby on Rails early on, but chose to use C# 3.0 mostly out of familiarity and a feeling by Chad & I that it was a low risk choice. 

How much of the TDD adoption obstacle is that our mainstream programming language isn’t entirely TDD-friendly?

 

Of course, everytime I curse Anders for making methods nonvirtual by default I remember that he saved us from checked exceptions and remembered to put first class delegate into the language from day one and all is forgiven.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.backlinkschecker.ws/backlinks/buylinks.html Purchase High PR Backlink

    I love this idea

  • Brian Johnston

    I respectively disagree, and that’s what it’s all about anyway – if there was a silver bullet we’d all use it.

    In my mileage myself, and coworkers, we have been burnt by inheriting other people’s service-locater/MVC applications that had hundreds of pieces. Lot of extensibility was built in, but the application was a memory hog, slow, and took forever to debug because everything was abstracted out to the nth degree.

    Obviously we all know that there are plenty of applications that this particular design works great – but it’s all in the bell curve, or as an old saying goes, too much salt ruins the pot. I think this is where we disagree; I see flexibility and extensibility as a bell curve with complexity and maintainability, and I get the feeling reading these comments that this is a purely linear formula to some, or it doesn’t drop off until the extreme degree.

    History repeats itself. Mark my words, in the next 2 to 5 years these same people who are pointing out the limitations of [language of choice here] will be complaining about the current [language/pattern of hype here] that they’re all on the same wagon about. How many of you remember Microsoft in ~2002 saying ASP.NET was a great example of MVP? Interesting how that’s changed in the last 2 to 5 years.

    @Chad:
    I’ve yet to have a test written properly (short, concise, focused on one function) get twisted in knots using Typemock. Perhaps that’s because I follow the TDD principals Kent Beck laid out in his TDD book and can also be found in Pragmatic Unit Testing. The only time my tests fail is when I either A) break the code accidently (good test then), B) change functionality or behavior of the method that is being tested – and no testing tool will get you around that.

    @Jeremy
    RE: I want you, and all the other TypeMock users, to remember one thing, depending on TypeMock to fake dependencies that you new() up directly in your classes is a VERY TIGHT RUNTIME COUPLING. You say that you’re only adding the extensibility that you need, but that statement is false. You’re only adding the extensibility that you THINK you’re going to need in the future.

    1. COUPLING != BAD in certain instances, lest I have an interface for ‘string’ or ‘Point’ – and who said I’m newing up anything? I may be using service-locator for all you know. Maybe I’m using Typemock for statics, or protected, or private methods because I don’t want to make things public that have no need to be and want things to be static, to be static.
    2. I don’t ‘think’ anything, I illicit requirements, sit in meetings with directors, technologists of other departments, managers, and other people who guide the 3-5 year strategic technology plan of the organization (do you have one?), and we collaborate/brain-storm/etc what level of extensibility, flexability, and scalability is required on a particular project based upon the expected lifetime of the application we’re writing (plus some time because everything lives longer than it was suppose to). I stopped being a cowboy coder and deciding what I thought was needed a very, very, long time ago. I also stopped trying to write applications that would last 10 years, and solving every potential problem that may arise several years ago – otherwise nothing would of gotten done because in 1998 I would of been trying to figure out how to get my classic ASP application to still be valid today (that would’ve been a waste of time and money huh?).

  • http://chadmyers.lostechies.com Chad Myers

    @Brian

    “my applications are only as flexible as they *need* to be.”

    And there’s the rub. I don’t know about you, but I’ve never been able to predict how flexible applications *need* to be in the long run and every time I’ve tried, I get bit in the rear end for it.

    The only successful strategy I’ve found for responding flexibly to app needs is by following good design principles (SOLID encompasses most of them and there are a few extras besides).

    TypeMock may help you with testing, but it won’t help you when you need to add new functionality in different ways and your code gets all twisted in knots and even your TypeMock-based tests become a convoluted mess.

  • http://chadmyers.lostechies.com Chad Myers

    @The Other Steve:

    While TDD may be a relatively new PRACTICE, the underlying concepts it helps to enforce and even guarantee are NOT new/hot topics. In fact, I would argue many people have gotten away from the fundamental principles of good design and a lot of these new, shiny frameworks are actually detrimental to your becoming a better software engineer and therefore you should avoid them, or keep them at arms length when using them.

    You can learn the fundamentals without TDD, but it sure makes it a lot easier!

  • http://chadmyers.lostechies.com Chad Myers

    @Brian:

    Good design actually isn’t that subjective. If you follow good principles, the rest of the concerns are easily conquerable.

    If you design for scalability, many other things become hard or impossible. I don’t know any project whose sole requirement is ‘scalability’, so you should not design specifically for scalability. You should design with the fundamental principles and scalability (which is subjective) can be adapted into the design according to the specific requirements that define “scalable” in your context.

    I wrote a post about this type of argument here:
    http://www.lostechies.com/blogs/chad_myers/archive/2008/08/18/good-design-is-not-subjective.aspx

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Brian,

    Let’s go way too generalized and say that a “good” design is one that maximizes return on investment. Scalability is important in *some* but by no means all systems. In absence of extreme needs for real time performance, scalability, or security, I’d say that the number one quality is maintainability. Testability is simply a (very big) factor in maintainability. Testability is largely a result of separation of concerns, cohesion, and loose coupling between different responsibilities in the system. The same qualities that lead to testability lead to making the system easier to understand and easier to modify over time.

    “‘ve also created more moving parts (or allowed the creation of), and I think common sense will tell us that the more moving parts we have (or can have), the more complex the system, and logically the more complex the system, the harder to maintain”

    Maybe, but I think that thinking is largely a myth. Given the choice between 100 cohesive classes with well defined responsibilities and 10 big classes that do lots of things, give me the 100 pieces. The only way to deal with real complexity is a divide and conquer strategy that allows you to do one thing at a time. Arguably, you could say that a good design structure is one that allows you to work with and understand one part of the system at a time. How often are you really changing an entire system? Most of the time changes *should* be isolated to one aspect of the system.

    I want you, and all the other TypeMock users, to remember one thing, depending on TypeMock to fake dependencies that you new() up directly in your classes is a VERY TIGHT RUNTIME COUPLING. You say that you’re only adding the extensibility that you need, but that statement is false. You’re only adding the extensibility that you THINK you’re going to need in the future.

  • Brian Johnston

    ‘Good’ is subjective. What *is* good design? Well that’s a very long discussion, but we can all say that a *good* design results in a product that is scalable? Is the only way to make a scalable application using interfaces and virtual methods? That’s a loaded question: we’ve all seen plenty of applications that were extensible, but crashed under 50 users. So let’s just look at the extensiblity aspect.

    If I added infinite extension points by using either virtual or interface driven, I’m very extensible, but I’ve also created more moving parts (or allowed the creation of), and I think common sense will tell us that the more moving parts we have (or can have), the more complex the system, and logically the more complex the system, the harder to maintain. Obviously there is a bell-curve there where no complexity means no flexibility, but on the other end, infinite flexibility gives no maintainability because it’s too complex to maintain at a realistic cost.

    Another aspect that we can agree on (I would hope) to *good* design is testability. If I can make an application that is extensible, without having to increase complexity using a tool such as Typemock, why wouldn’t I? This is the exact reason I use Typemock – my applications are only as flexible as they *need* to be. So rather than making a more complex product for my testing framework by making everything interface, public, and virtual driven, I design the product for the client with the flexibility they need and my testing framework (Typemock among others) become an unobtrusive tool that can thoroughly test the product.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Roy,

    I have no problem with TypeMock existing, I just don’t feel like its abilities are that important in the greater scheme of things. If you’ll actually bother to read what I’ve written about TypeMock you’ll see that I’m directly criticizing the rhetoric from you and Eli and the gang. I think you are all confusing “it’s technically possible to write a test for this code” with “this design is good”

    Besides, depending on your product for mocking instead of using DI still leaves you with tightly coupled code and eliminates possibilities for reuse and evolutionary design. Using TypeMock is a compromise.

    My point of view on TypeMock (that you and your cohorts either won’t admit or refuse to recognize) is that it will not help you all that much with bad designs.

    “moving to a less friction language will achieve the same conditions that, by your theory, will produce the same bad design you fear.”

    I don’t think that makes the slightest bit of sense if you think about it. Take Ruby for example, all the mockability and *swappability* that I get with DI in C# comes for free in Ruby. C# + TypeMock isn’t as good of an answer in this regard as Ruby.

    Plus there are other advantages you’re not accounting for. Simply switching to an interpreted language makes the TDD cycle much, much faster. And remember that Ruby is much more terse than C#.

    On the F# side, doing purely functional programming means that you wouldn’t have to bother setting up any mock objects at all.

  • The Other Steve

    I don’t know. I read these things and it makes me realize how stupid I am. I’m not really that dumb of a person, but I just can’t keep track of all the little nuances. By the time I’ve mastered the latest buzz, the world has moved on to the next one. Now imagine trying to hire someone who understands this? It ain’t easy.

    I think people need to come down to reality, spend a few months working with the crappy code we get from outsourcing projects to India.

    Maybe the answer to this is dynamic languages. I don’t remember having nearly as much trouble identifying the right way to do things with MSBASIC back in 1982. :-)

  • Roy Osherove

    So, what is C# was testable by default? like Ruby?
    That would remove the friction, right?
    So if a product such as (you know) helps achieve that, why in the world do you still have a problem with it existing, claiming that it will ruin good design guidelines?
    moving to a less friction language will achieve the same conditions that, by your theory, will produce the same bad design you fear.

  • http://persistall.com Brian Donahue

    I’m sort of glad to hear you’re having the same questions, because it was Chad’s and Joshua’s posts about some of the stuff you were working around on MVC that started me down a path of wondering just how much overhead I was willing to deal with to create clean, testable designs in C#. I definitely am going to have my eyes open for opportunities to do more ruby stuff in the near future, so that I can get better versed in ruby and have a better basis for comparison.

  • http://weblogs.asp.net/ysolodkyy/ yuriy

    While I do not see a big problem with writing testable code in C# I would definitely try to switch to F#. C# is good if you think about testing from the beginning and you will likely be able to test what you need without any special frameworks for mocking etc. Homegrown set of utilities and nunit will likely be sufficcient. So, just think about testing when you design the system.
    However, F# gives us some very useful things like “computation expressions” which allow wrapping code (add special handling) for testing purposes like ensure invariants on each step or simulating real user behavior.

  • http://blowmage.com/ Mike Moore

    I am on the record that I believe that C# has too much friction and ceremony and it’s days are numbered. C# will continue to be used, just as C++ continues to be used, but dynamic (and functional) languages will become the dominant approach for application development. And for all the right reasons, which includes testability.

    http://blowmage.com/2007/12/18/the-rubification-of-csharp
    http://blowmage.com/2007/12/19/static-languages-are-fail

  • http://blowmage.com/ Mike Moore

    “a feeling by Chad & I that it was a low risk choice”

    How very courageous of you both. :)

  • Kerry MacLean

    I’m not sure that language limitations are the primary obstacle to TDD development – I think it’s legacy coding habits. It’s difficult to change what you already know, and in most cases, you have to challenge not only your own habits, but those of your co-workers as well.

    If you work at a larger company, you also have to overcome management’s preconceptions, which probably were instrumental in getting to .NET in the first place.

    For many of us, adopting new languages to ease the TDD friction is a hoped-for, brighter future, not something that I could realistically consider today.

  • http://kevin-berridge.blogspot.com Kevin Berridge

    I had the same thought at one point. http://kevin-berridge.blogspot.com/2008/07/static-languages-dont-trust-you.html

    But like you say, there’s a cost and risk in switching. Not to mention that in the corporate world, it’s not a private decision, you have to switch everyone.

    But I think that static languages do hinder TDD adoption. Mainly because it requires using the language in a way that is non obvious.

  • Mindaugas

    Regarding C# and TDD:

    Testing private/protected:
    http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

    I never found C# beeing a hard language for TDD. While there are some problems, it’s still doable. Of course, it’s far from perfect and behind some other languages you named.
    But for me, there’s one tool (are there anything similar for other languages? – i’m not aware), that makes C#-TDD for me really great – it’s Resharper. The pluses of Resharper for me outweigh the lower-friction of other languages. I would consider switching, if I would feel, that I would be more productive with other languages. But again – at this time, I’m simply more productive sticking with C# and Resharper.

    Mindaugas, Software Engineering Student, Lithuania