I'm not a TDD person...or at least I wasn't until last week. Up until then, I had read the blogs and looked at the examples to try to understand TDD and unit testing (with mocking) in general. Almost all of the examples I was shown demonstrated very basic scenarios that, in most cases, were too trivial to show value. I would ask people who would speak about unit testing in general how you'd do a specific scenario and would get mix responses ranging from "just try it" to "you should be using this tool and it'll just write the tests for you".
Last week, I had some free time to where I could truly evaluate a code base I finished up with the prior week and see if I could apply some tests to it. The code I wanted to add tests to were simple repositories where I had abstracted the database calls to separate classes. Being told to mock the databases out in the past, I started to do that and focus solely on these repositories. In some instances, it felt like I was testing the abilities of the mocking framework instead of my code but these methods were simple by design (i.e. "Get Customer by Id 'X'"). It was when I started to test more of the methods that I saw that some of the code I wrote had some issues.
The code was functional; however, it wasn't easily testable. When I would attempt to describe the test, I found that the code did more than one things (breaking the Single Responsibility Principle). Another incident showed that the name of 1 whole class didn't make sense. A third incident showed me an issue with the number of complexities that come from multiple dependencies and object inheritance. After each time that I finished refactoring these issues out of the code and eventually got to working tests, I began to see how the questions that TDD (and BDD) cause you to ask while designing an application really shine.
One example of this was a duplication of code. I had a class called, for the sake of this post, UserRepository. The UserRepository had a method to get all users in the system and returned a List<User> back to the calling code. This class also had a second method that did the same thing but the List<User> only had the FirstName and LastName properties populated. Why did I write such months ago? Short answer - I have no idea. Looking through the code, I found that a different feature needed just the FirstName and LastName properties and nothing else...it was then sending the List<User> to the client for an AJAX call. While I can understand this now, I could have better implemented the solution by if I looked at the scenario and behavior better initially.
If you ever find yourself wondering how writing more code for TDD or just basic unit tests can actually help the code, my recommendation is to just try it. Take a simple class that has only a few pieces of logic in it and test it. Next, take a simple class that has a dependency that you'll need to mock out and test that too. After you understand how to write these basic types of tests, you can move into the self-reflective questions that really shine in TDD. Questions like "What type of data do I want to work with when I call method X?", "What will this class need (a.k.a. dependencies) to get me data Y?", and "Is this functionality already present?" are all questions that get forgotten while we're sometimes blindly coding.
Looking ahead, I can only imagine how my code will be looking.
The funny thing about TDD is that you never seem to stop getting those "oh wait, so THAT'S the real point" moments as you work forward through the benefits of using it.
ReplyDeleteI find it extremely hard to use for everything as I'm just not that fluid in it yet. But I've gone back to 9 month old code bases and been able to actually understand them (rather than just 'trust that they work') because of the simple atomic unit tests that accompany them.
That in itself is worth it even if I got no other benefit from the practice.
From the way you described it, it sounds like you were really just finding the value of unit tests, not TDD. TDD isn't something that can be done to code that you've already written, though you can get close when writing tests while refactoring. TDD means you write tests before you write code.
ReplyDeleteI'm glad to see you've found benefits in unit testing, and I highly recommend you give the "test first" approach a try the next time you're able to. It is challenging, but even more rewarding than "test after"!
@Troy Goode
ReplyDeleteWhile I describe more of my experience on understanding unit testing in general (and that unit testing is not the same as TDD), the experience through refactoring a current codebase has provided me the insight into the test first and design elements that are generated through practicing TDD.
I've seen the value in unit testing for a while now; however, have had troubles until just recently connecting unit testing with legacy code. In addition this has helped me to see how doing tests first as advocated by TDD provides a paradigm that helps force a person into writing good code initially.
The other aspect of this understanding is that I was one of those people who work a lot in legacy code and trying to map TDD practices and just plain unit tests to existing codebases. It really wasn't until truly finding an effective way of adding unit tests to the legacy code where I saw the huge benefits that TDD has when it's used from the beginning.
Yeah, refactoring is about your only option for introducing TDD to a legacy app (assuming it is in maintenance mode), and is significantly more complicated to do due to existing dependencies that likely were not managed well.
ReplyDeleteHopefully you'll run across a juicy greenfield app to try it out on soon!
Duh! How long did it take you to understand the merits of OO when it first came out?
ReplyDeleteI'm only kidding ;) Seriously, I am glad you did. I strongly believe in Software Craftsmanship and to me TDD is the key to get the joy back into our daily work. I think getting to a good design that agilely and naturally evolve with ages is not possible without TDD because not many will dare to refactor a legacy code base and we never get the perfect design right from the start. Without TDD, we end up earning skills with puzzle resolution instead of OO design.
I love coding and design, but the situation mentioned above made me want to change my career orientation at some points. After I worked on huge systems that were written without TDD (nor much respect) I get a little upset with stubborn people ;) XP came out 10 years ago and there are so many books presentation, blogs and articles. But better late than never.
I gave so much energy to explain the benefits of TDD and other XP practices and with so little success! I was even there to pair, I made helper classes to test over a chunky system, I did talk about it to every developer at every occasion I had, I wrote some short doc to document the good practices (starting with the philosophy and a huge list of benefits). I have always have been so much into it that I think I could talk about the ROI for 2 days in a row without getting tired.
At my previous employer, I ended up going crazy and I had to leave this my place. I worked with experienced XP people in the past and I think there's plenty of places out there working this way. On the other hand, I think some people are just stubborn. Or they are comfortable into their little routine and they just don't want to bother trying something new when they can still get their pay check without it.
I am a skeptical person and I have a very critical mind but sometimes, I just believe what I read when it comes from notorious authors. Then I take my courage and I execute it on a project at work. In the case of TDD, I early understood the beauty of it!
I think they should start teaching it very early to student programmers. Last time I looked for job opportunities TDD experience was a requirement almost everywhere.
Sorry for this long comment and thanks for this post. I hope it will help more people to get into it.
By the way, here's the next cool tool I want to learn for BDD: http://cukes.info/
Congrats, J.! The TDD epiphany moment is truly a wonderful thing. My coding life has never been the same since.
ReplyDeleteAwesome! TDD is great, I couldn't write code without it. Keep it up!
ReplyDelete