When I started using TDD, I found that writing unit-tests for business logic is straightforward. But what about all the rest? As a developer using ASP.NET MVC, what about my views and controllers?
It took me quite some time to figure out all my missing pieces to use TDD in my daily work.
I'm planning to write a couple of blog posts about, what I call, "Enterprise TDD". With them, I want to help beginners getting started with TDD in real world scenarios.
For these blog posts, I am collecting pain points, lessons learned and "missing pieces", when it comes to "real world TDD". I would love to hear about your experience.
Top comments (20)
TDD is a long-term investment, with a guaranteed ROI. imagine how many bugs would you avoid.
Hi Lars, great topic.
Some random thoughts:
Hi Blaine,
thanks for your thoughts on that topic. I totally agree with your points.
And I think you are totally right with your last point. There are so many "programming is easy" posts on the internet. But the reality is: It's not. Of course it's not wizardry. Most people can learn it. But starting with TDD would be too much.
Hello Blaine,
I agree with you. But one little moment. In the last point you're mention that programs is overwhelming - it's ok!
But, I think that TDD in the beginning of the way it's good practice.
Yes it's difficult to understand, but it sets good tone of you code.
Even if it's hard for beginners, it is better to practice it from the very beginning.
P.s sorry for my English.
My (limited) experience has been that programming is absolutely overwhelming and that beginners progress faster if you can reduce the cognitive load as much as possible.
So I would try to get them comfortable with the basics, once that's achieved, you can add an IDE, then static analysis, then unit tests. And once they get unit tests, you can add TDD.
If someone dropped me in a brand new language with completely new tools and an unfamiliar IDE, I'd probably resist writing in TDD from the start. It's just way too much.
But, that's just my experience. If you've had good success starting with TDD, that's great.
My problems? Managers and Architects who tell me we don't need tests!
I can see your point. But I see writing tests as part of my job. And I don't think developers should ask for permission to write tests. αΊill developers actually be punished for writing tests "on their own" in your environment?
Even if you follow "writing tests as part of my job" it only works if other developers write tests to, or else they will keep breaking you work you previously done.
I agree. Testing should be an environmental decision, but I still would write tests for my code if I were to work in a non-testing team.
Uncle Bob said something similar about doing TDD even though your peers don't do it and your managers are against it, though it's easier said than done in some cases.
"expect it to take way longer than you think initially" -- definitely agree with Blaine on that one
I think the biggest lesson I've learned recently is to actually listen to your testing pains instead of ignoring them. When I find tests hard to write, it's usually for one of a few reasons:
That is so true. That quote will definitely make it into my blog posts, if it's ok for you. :-)
Disclaimer: I am not myself a total TDD enthousiast. I do not write large chunks of code driven by tests etc... I have my reasons for this however I do recognize some benefit to TDD and those that are good at it do write good code ...
Anyway, one part of TDD that is not the core of TDD but that seems to me like a nice way to dive in TDD (and I happen to actually do this even if I do not TDD the rest of my work), a way which prevents that awkward part of having an empty file and writing code that is obviously wrong just to comply with TDD.
Here it is: when you need to fix a bug on an existing project: do not fix the bug but write code that triggers that bugs, write that unit test and make sure it fails and only after that fix the bug.
Great point. It's so important to write a test for reproducing the bug, before you fix it. It's the only way to make sure, that the same bug will not get into production anymore. I have seen the same bug get into production more than once.
I think TDD is a great tool to work towards a known solution. More specifically, if the general approach is fixed and can easily be split down in smaller chunks.
In contrast, when exploring the solution space of a problem or a new technology, TDD can be more of a distraction. In this case, I prefer working towards a working solution before (!) figuring out how to test it.
Something else that I find important: TDD is especially useful when pairing, since then, it additionally serves as a great communication tool. Sometimes, a test can help to communicate an idea much better than words.
I can't stress enough that TDD is one tool that helps us writing code that's both tested and testable. Nevertheless, the focus should be on the goals, not the process.
Blindly applying TDD can lead to code that's well tested and testable, but hard to reason about, especially if continuous refactoring is neglected.
Understanding what makes up testability and reasonability should be the primary goal for anyone who's learning TDD.
Hi Tobias,
thanks for your feedback.
That's true. The goals are more valuable than the process. Good statement.
I don't fully agree here. When it comes to new technologies, I agree. But if I am working towards an unknown solution, I find TDD pretty helpful. Using TDD, I can focus on the requirements and that helps me find a solution.
Amen. :-)
My problem for doing real-world TDD?
Well, foremost is tooling.
Some languages are not very amenable to TDD.
C++ works hard to thwart the "write test, run failing test, implement, run passing test, refactor, check-in" one-minute cycle, when the edit-build-run cycle is 30 minutes long.
Some version control systems are not very amenable to TDD.
TFS/TFVC, Perforce, Source Depot. Basically any version control system that is used by the business for administor purposes rather than for developer purposes.
If you can't change a line of code and check-in on a minute-by-minute basis -- even for a change that will (temporarily, I hope!) make for broken code -- the version control system is greatly inhibiting development.
Close second is cultural.
Some team cultures bear strong resistance to TDD.
Because change is painful (stressful). Learning new coding habits (such as TDD) is like trying to learn a Dvorak keyboard layout... all of a sudden 90 wpm turns into 5 wpm.
Some management sees no value to TDD.
So that management either does not support it, or actively opposes it. Especially command-and-control style management... if they're not onboard, TDD is doomed.
Thirdly, TDD done wrong.
I've seen way too many "unit tests" that were really integration tests.
I've seen way too many unit tests that were written in arrears. Which isn't TDD. And short-circuits the key benefit to TDD: tests guiding implementation design.
TDD puts positive pressure on a developer to abide by several of the SOLID principles, DRY principle, KISS principle, YAGNI principle, and Clean Code. If you've done a lot of TDD, you probably have noticed that most of your TDD methods are functionally pure with all the benefits that confers... and that's no accident.
But if the unit tests are written in arrears, all of that goes out the window.
In the real world, what did work?
When I worked with a team that was TDD savvy, and used C#, Visual Studio, NUnit and NCrunch... it was like magic. The right language, with mostly the right environment, with a reasonable unit testing framework, and the magic pixie dust of NCrunch was worth its weight in gold.
What was wrong with the environment? I would switched from TFS/TFVC to TFS/Git.
What was wrong with the unit testing framework? I would have preferred xUnit.net over NUnit, but such is life. NUnit is okay.
What was wrong with the language? It was a .NET project, so C# was very suitable. I would have preferred F#, but that's because I've tasted the functional programming flavorade and I like it.
Outside of that project, I would strongly consider D because of its core language support for unit tests and design-by-contract programming. Having design-by-contract as part of the core language can go a great way to needing less unit tests. Having unit test as part of the core language means you can change unit tests runners at will without all the unit tests needing to be re-written because each unit test framework has a different syntax and facilities.
Well, that my 2Β’ worth. :-)
Hi Eljay,
Thanks for the great feedback.
I agree that tooling is an important point. I'm happy, that I can use C# and NUnit for my daily work. Even if I, just like you, would prefer xUnit.NET.
And a really critical part is the team. If your team is not willing to try TDD or, even worse, doesn't like unit tests at all, you're lost.
I haven't seen a lot of managers who would actively forbid writing unit tests. Most of the managers I have worked with, would not tell you to write unit tests, but they would not punish you, if you do AND get your stuff done.
I can totally see your point with "TDD done wrong", but in most cases I would state that integration tests are better than having no tests at all. :-)
I like your recommendation for using D. I have never really thought about using D as a programming language. But with your points in mind, I will get a closer look at that.
Yes, I had a huge problem, it doesn't exists, at least in my "real world", outside of the corporate area or simple 100 LOC examples there are only a few areas it can be applied.
In the Real world we have legacy code, undecided business logic, coworkers that do not want to make tests and product owners that explicitly ignore the benefits, we also have projects with an uncertainty life span.
Anyway I have just one request, please do not make the same mistake as most of the religious articles I keep seeing: attributing all the glory to TDD.
Most of the benefits, ROI, less bugs, able to refactor, code flexibility, acting as code documentation, ....... are the results of doing "automatic tests" that includes unit tests, not because you apply one methodology like TDD or BDD.
Make a distinction between Unit tests and how you can write them. There are some benefits from doing TDD but in comparison to the entire AutomaticTests picture is a small list.
Hi BG Adrian,
I'm absolutely with you. This post / discussion is not meant to show TDD as a cure for everything.
You are right, that most of the benefits, like ability to refactor your code safely, are results of your test suite. It's important to point that out. Thanks for your feedback.
But: TDD is a great way to ensure this test suite. If you follow the TDD cycle, you get the benefits of an automated test suite "for free". As some of the community members already pointed out, writing tests after writing your code, is a pain in a lot of cases. And that's why a lot of people don't write tests at all.
But once again: Automated tests are the biggest / the main benefits of TDD.
Writing tests after you are done writing the code is always a pain.
If you can, write tests first. It makes you think about the API upfront. The code will not only be easier to test but also easier to reason about. And you have to do less manual testing since you have the automated tests ready. It also feels much more rewarding making the tests pass little by little than treating them as an afterthought.