DEV Community

Cover image for Getting Started with Test Driven Development

Getting Started with Test Driven Development

Chase Stevens on January 18, 2019

Test Driven Development is considered to be a best practice in the software industry, yet many teams ship code without tests. What it is ...
Collapse
 
codemouse92 profile image
Jason C. McDonald • Edited

Although I believe strongly in testing, the TDD philosophy has never worked for me. I'm sure it is helpful for many others, so I would never go as far as to say "don't do it," but I would like to advise that one not embrace it blindly either.

In my 8+ years of programming, I've found that most of Uncle Bob's advice needs to be taken with a very large grain of salt, especially as he has a habit of conflating his personal opinion and emotional bias with fact, and then touting the result as the One True Way™. Time has proven over and over again that, in programming, "best practice" is a purely mythological beast. No practice or methodology is beneficial to every project; if something is helpful to one project, you can be certain it will be proportionally detrimental to another. TDD is no exception to this.

With that said, I do believe all projects require some form of testing, but there are dozens of equally viable methodologies and approaches to this.

Like I said, I don't want to claim that TDD is somehow "bad" or "wrong," but I want to caution that it is not a magic bullet, and its suitability to any project or team should be carefully and critically evaluated.

By the way, I detailed my own testing habits in a comment on this article (below), which is a great companion to yours, I might add. Some may disagree with my way of doing things, but I have written years worth of stable, maintainable production code, with surprisingly little technical debt. My methods work for me, and the proof is in the pudding. (Your pudding may well be different.)

Collapse
 
mattconway1984 profile image
Matthew Conway

Depends on the implementation, I write a lot of multi threaded code and functions which spawn threads to run control loops, they are very difficult to TDD, in fact, I would say, impossible! You end up writing tests that are more complex than the code itself and completely tie down the implementation, so they are not good tests. You can exploit encapsulation by exposing "hidden" implementation.

Collapse
 
danielfoxp2 profile image
danielfoxp2

Inside each thread there is sequential code, right? What is the impossibility to implement these sequentials code using TDD?

Collapse
 
danielfoxp2 profile image
danielfoxp2

"Although I believe strongly in testing, the TDD philosophy has never worked for me."

It is not an attack on you or making you less professional or capable. It is just a possibility backed by your own words. So don't get me wrong, okay?!

Maybe TDD never worked for you because you are relating TDD with testing. TDD is not about or for testing. TDD is a tool that you use to analise the macro and micro requirements of the solution you need to deliver. Not only that, TDD will help you to design this solution and on top of that you gain confidence to manipulate your code, adding, removing, refactoring, things around. You ending up with a test suite is just a collateral effect, a good one, but collateral nonetheless.

TDD is not and shouldn't be a replacement to Q.A. and shouldn't be considered part of it too.

Answering the question of your article title:

What we should expect from tests?
A lot of different things. But this has nothing to do with TDD.

Collapse
 
codemouse92 profile image
Jason C. McDonald • Edited

No, I appreciate the feedback. I can assure you that yes, I do entirely understand what TDD is (have for at least half my career), and while the two are inexorably related (ergo TEST-driven), you're correct that they're not the exact same thing.

(Yet, perhaps most annoyingly, many TDD-advocates I've read/spoken to over the years blur the line between testing and TDD, as if you have to use TDD to do testing.)

TDD is all about planning, designing, and then with tests as one of your main tools to that end, and that approach simply has never worked for me. I've tried it.

It's also form of functionality-based design, which had already come to be considered inferior to model-based design back when the most exciting part of Robert Martin's weekday was still "recess". (Although there are still situations where functionality-based design has its place.)

Again, as I said, that doesn't mean TDD doesn't work, or that it shouldn't be used. If TDD works for you, use it. But it isn't a magic bullet. It does not fit every situation or every project, no matter how perfectly you implement it. (And, in fact, there are no magic bullets.)

P.S. That's not my article. Like I said, I merely detailed my own testing habits in a comment there.

Collapse
 
rossdrew profile image
Ross • Edited

Firstly, TDD is universally misunderstood.

creating software by writing tests before writing code.

This is the most frequent misunderstanding. TDD is not writing tests then writing functionality. It is writing a test for the absolute simplest case, fixing that test then adding a test case. Therefore, testing is done alongside your code. I would have maybe accepted "creating software by writing a test before writing code." but "tests", no.

Take the example that you want to write a program that parses roman numerals.

  • You write the test myParser.parse("I") == 1
  • You write a RomanNumeralParser class
  • You add a method to parse
  • You write the functionality "if string.equals("I") return 1"

It's also important to note (as without it, it's not TDD) that Test Driven Development requires that

  • Your tests are unit tests
    • They are quick and standalone
  • You write the absolute minimal code to satisfy requirements
  • You refactor as you go

People who are against TDD have usually been exposed to it in the wrong way. It should be done correctly and strictly for a period of time, once it shows you how many assumptions you make in programming you then loosen how strictly you follow TDD. It's not a methodology to be followed, it's a practice to show you how to test properly and the benefits of fast, contained, independent unit tests.

Collapse
 
danielfoxp2 profile image
danielfoxp2 • Edited

TDD doesn't require your spec to be an unit test. It is desirable to have the majority of your specs as unit, but it is not a commandament.

"it's a practice to show you how to test properly and the benefits of fast, contained, independent unit tests."

No. It is to help you analise requirements and design your software to meet those requirements. It is a development tool, it is not a test tool and it is not for tests. It helps with confidence to change code, refactoring, adding, removing, code. It helps you discover simple implementations to solve the current problem (test).

The first 2 steps, add a failing spec and making it pass is to build confidence, the third one, refactor, is to improve design applying best practices, patterns and so on, with the confidence that your changes is not breaking any desired and already existent behavior.

Test is another realm with tons of others methodology and practices that should not be confused with TDD. Ever.

Collapse
 
codemouse92 profile image
Jason C. McDonald • Edited

You may consider just writing a full article on TDD, @danielfoxp2 . It'd get more views, and more robust feedback, than just the comments section of another article. Just click WRITE A POST in the upper-right here on DEV.

(Although, to reiterate from earlier; you haven't posted anything that's news to me about this, yet TDD still doesn't work for me. If you explained it, it might make better sense and work for someone else, though!)

Thread Thread
 
danielfoxp2 profile image
danielfoxp2

Thanks... I'm creating courage to start it =).

Collapse
 
rossdrew profile image
Ross • Edited

TDD doesn't require your spec to be an unit test.

No but 99% will be because rapid development with system tests is hard and in many cases impossible. Rapid development with integration tests is just hard. The more components involved, the slower development gets and TDD is about fast development cycle.
Who said it was a "commandment"? It's common sense. If the majority of (and preferably all) your TDD is not unit tests, you are not doing TDD.

Test Driven Development [...] "is not for tests".

"Test[s] [...] should not be confused with Test Driven Development.

Classic quotes!

It helps with confidence to change code, refactoring, adding, removing, code

Those are side effects of testing properly and having good test coverage. If you do TDD then remove the tests ALL of these benefits go away. So how you can say TDD is not about tests when without them, it's not TDD is beyond me.

You are confusing test frameworks and QA with code tests and somehow conflating them with TDD.

Thread Thread
 
danielfoxp2 profile image
danielfoxp2 • Edited

I'm not cofusing anything. I never mentioned any framework either.

You can do TDD with zero unit tests and it still is TDD. TDD is the practice of write your behavior spec before the production code. There's no rule or consensus anywhere stating that if you're not using unit tests then it is not TDD.

I have not the time now to address your others arguments but I'll leave you with the names of two good articles, one from IEEE, Kent Beck, 2001: "Aim, Fire" and the other from Dave Astels, 2005: "A New Look at Test Driven Development".

They have reasons to say what they said.

Thread Thread
 
rossdrew profile image
Ross

TDD is the practice of write your behavior spec before the production code

Well, no. Unless you consider a first draft of a small piece of code to be "production code". You add A test, then add a small incremental piece of functionality. That is not production code yet. If you write ALL your tests first, then all your "production code" then it is not TDD because there's a 99.99% change you have messed up many requirements/specs (as you can't know all in advance, hence the origin of incremental development) and you have slowed yourself down.

There's no rule or consensus anywhere stating that if you're not using unit tests then it is not TDD.

No, there isn't but there is a rule that says "very short development cycle" and if you want to remove the "very" and make it integration tests, you've started to lose focus on TDD, if you also remove "short" by making it system tests then all you have is "development cycle" and it's arguably no longer TDD.
You will also find in most literature that talks of TDD, they talk about units and if you are testing units with something more than unit tests, you're probably not writing units and you're writing something that is hard to test, hard to refactor and likely hard to reason about.

Given that you are conflating terms such as "production code", "unit" and expecting every action to be excluded unless explicitly defined

Ah, now you are trying to educate me by providing resources, cool:

IEEE, Kent Beck, 2001: "Aim, Fire"

I'm not sure which of your assertions you think this supports given that it is a three page article in which he uses unit tests and describes TDD as "test first". So looks like you just googled "TDD articles" and posted it thinking you were right and it would just support you as a side effect of that.

Dave Astels, 2005: "A New Look at Test Driven Development"

As for this article by a relatively nobody, which says he doesn't believe in units; take a look at the adoption in the industry of this technique. It's close to zero. Whereas entire systems, companies and frameworks have popped up before and since around units. So Dave might not like it but the industry doesn't care.
Interestingly, all of his recommended TDD books are in stark contrast to his views on units. So...pinch of salt.

They have reasons to say what they said.

The first one said nothing, second one...is clearly confused.

Thread Thread
 
danielfoxp2 profile image
danielfoxp2 • Edited

Well, no. Unless you consider a first draft of a small piece of code to be "production code". You add A test, then add a small incremental piece of functionality. That is not production code yet.

That was not what I said.

There is "two source codes" when you are developing a system using TDD. The one where you describes your specifications (tests if you like) and the one you deliver to the users, where the specifications will be implemented. The one you deliver is what I mean by production code in that context. I said nothing about the status of the "production" code, if was done or not. That's because I assumed that you would be at the same context level I was. I just hope that it was not an intentional straw man from you.

No, there isn't

There is no "but". Your claim is false. Period.

but there is a rule that says "very short development cycle"

This is not a rule. Neither Kent Beck or Uncle Bob accepted definitions of rules to TDD states what you said.

There is, yes, a desirable preference for very short development cycle. It is not a rule. But even if it was, what is the measure of "very short"? 1 week? 1 day? 1 hour? 1 minute? 100ms (like Gary Bernhardt)?

If you do an integration test that take 10s to run to exercise one function of the production code that you are implementing, this would be fast or slow, short or long?

It depends on the referential. If to do the same test you need to open a browser, fill up dozen fields, select the context you are trying to get right and click in a submit button, maybe it cost you 50s or more. So 10s seems really fast and short in this case, right?

And if your whole suite takes 7 minutes to run, it seems pretty bad right? But how much time would you waist doing it manually? And what if you forget some context?

If you have 200 integration tests that takes 10 seconds to run each, it could cost you 30 minutes to run them all. Yeah, that is not "very short" time to wait to get feedback. But what if you run them in parallel, using all power of your pc, achieving a result in 15, 20s? Is it not short? Is it not acceptable?

Again, your claim that TDD without unit tests is not TDD is false. This rule doesn't exists and you are defending it without any proof or evidence.

You will also find in most literature that talks of TDD, they talk about units and if you are testing units with something more than unit tests, you're probably not writing units and you're writing something that is hard to test, hard to refactor and likely hard to reason about.

There is another problem in definitions. What is the unit definition? 1 isolated function? 2 classes operating together to achieve some business behavior? What is it?

Some people are more or less restrictive than others in this matter. The Uncle Bob itself is one that had said a while ago that if you need to exercise a behavior that call the db, this is your unit in that context, making it a unit test.

I disagree with him and consider this one being an integration test, but I understood his point. In that context the minimum unit to exercise the behavior involved a call to DB and that was the Unit under test.

For me, an unit test does not do IO, DB, Internet. Which makes it very fast. But in nowhere was stated that unit test is the only way to TDD.

Given that you are conflating terms such as "production code", "unit" and expecting every action to be excluded unless explicitly defined

I conflated nothing. Probably I've explained myself poorly (which I think was the case). But there's another possibility, given your tone in the next quotation, that you just built a very big and strong straw man and now is fighting it to show the great warrior you are (if this is the case, I'll stop. Don't have time for ego trip).

Ah, now you are trying to educate me by providing resources, cool:

Nah, this was not my intention. As I wasn't able to write a longer answer at that moment I didn't want to leave my answer without elements showing the point I was making. The resources was just to enrich our discussion not a weapon to hurt your ego.

So looks like you just googled "TDD articles"

Not man. I was saying that TDD is not about tests. This reference, of Kent Beck, shows the man himself stating the same thing and explaining why, thing that I couldn't do at that time, the important thing here is that the reference explain WHY I said TDD is not about tests (again it was not a weapon to hurt your feelings). "All your testing gurus are sharpening your knives..." (something like that, I am quoting from memory).

As for this article by a relatively nobody, which says he doesn't believe in units; take a look at the adoption in the industry of this technique. It's close to zero.

I don't know if you understood what he meant. But I will assume that you did. I'll address the "relatively nobody" and "close to zero (adoption)" pieces.

Do you know the market share of the ruby made systems? Do you know a tool called RSpec? Do you know the market share of RSpec in the Ruby community? I don't know too. But, with some google searches we can suppose that is an important number and very away from zero, don't we?

Do you know Javascript? Have you ever heard about Jasmine.js? Mocha? Chai? Jest? Yeah. All of them was influenced by RSpec, which was influenced by this relatively nobody guy.

What about BDD, have you heard about it? Yeah, this guy was one of its "creators".

dannorth.net/introducing-bdd/

Don't know if your ego will have problem with this resource too. I am just backing up my points. Sorry if it bother you.

Thread Thread
 
danielfoxp2 profile image
danielfoxp2 • Edited

And before you come with the 3 rules by uncle bob with the word "unit tests" in it, take a look at this:

twitter.com/unclebobmartin/status/...

Some TDD tests aren’t small. Some TDD tests look more like integration or acceptance tests. There really aren’t any rules in that regard. So perhaps the best name of all is simply:TDD tests. Why? Because TDD tests are unique.

twitter.com/unclebobmartin/status/...

We use the term “unit” test to describe the tests we write while using TDD. But that name is unfortunate because it has no good definition. We don’t know what a unit really is.
The tests we write in TDD are written by programmers and for programmers.

twitter.com/unclebobmartin/status/...

DAO tests are unit tests.

twitter.com/unclebobmartin/status/...

There is no rule that says that unit tests can’t touch the database. Of course they can. One should be very careful, however, to avoid touching the database when the database is not relevant to the test.

Cheers.

Collapse
 
eljayadobe profile image
Eljay-Adobe

When I was programming in C#, using Visual Studio, NUnit, and NCrunch, we wrote TDD style unit tests. Not only did we have pretty good unit test coverage (about 70%), writing the tests was actually fun. Yes: fun. (Now only if I could put that in a 96 point font, with the blink tag.)

I attribute the fun factor entirely to NCrunch: it is pure magic. When writing TDD style unit tests becomes fun, unit tests get written.

These days I'm programming in C++. By-and-large, we don't have unit tests. We do have a few, but not many, using Google Test. Writing unit tests in C++ is not fun. There's nothing equivalent to NCrunch for C++. Google Test or alternatives like Boost Test Library are awkward to use, even though they are well thought out... they're still a bolt-on to the language and rely heavily on a lot of macro-magic to get the job done. But doing TDD style "write the test, run-and-fail the test, write the code, run-and-pass the test, refactor the code, run-and-pass the test, check-in" cycle doesn't work when the compile-then-run-tests takes 20 minutes. :sad panda: (C++20 will have contracts, which will help a lot. Funky syntax, though.)

One of my favorite languages is D. Two neat features of D is that it has contract support built into it, and unit testing built into it. Between those two features, I found writing the precondition contracts, postcondition contracts, and invariant contracts to be fun, and writing the unit tests to also be fun. Having contract support means there are a lot of simple sanity unit tests that do not need to be written, so the quantity of unit tests is a lot smaller. The syntax is very straightforward, and since it is part of the core language one doesn't have to get the team to agree upon some bolt-on unit testing framework.

For me at least, the upshot is: some languages that are much more amenable to TDD style unit testing. Other languages do not lend themselves to TDD style unit testing.

Unit tests (as a residual value) ensure basic correctness, especially for procedural and object-oriented programming wherein ensuring basic correctness is otherwise difficult. They are no substitute for automated integration tests and automated system tests. And, vice versa, integration and system tests cannot be used as a substitute for unit tests. Different domains, by different people, for different purposes. Unit tests are written by developers primarily as a design tool to aid development, with residual value as a regression suite to ensure basic correctness. (Regardless of TDD style unit tests, projects still need architecture. Unit tests are design-in-the-small.) Integration tests and system tests are written by quality engineers primarily to ensure the separate parts work together as expected.

PS: when I say "unit test" I mean that in the TDD sense. To me, things that are not TDD style unit tests are not unit tests, but the term "unit tests" is commonly used in the industry to refer to things that I would categorize as integration tests, or system tests, or performance tests, or security tests, or smoke tests, or usability tests, or acceptance tests, or bug regression tests (which are a usability tests variant). Best to be aware of the possible miscommunication due to same terminology with different semantics.

Collapse
 
glinren profile image
Glinren

While I have given up on TDD, I have to say that with the Catch family of unittesting frameworks (currently I am using doctest) C++ has the nicest unittest experience for me. To the point that I do miss doctest when programming in python.

Collapse
 
eljayadobe profile image
Eljay-Adobe

I've looked at Catch before. I like it, nice to see that Catch2 is still an ongoing endeavor. It is straightforward and easy to jump in and start using right away.

The single-header-file approach helps to lower the barrier to entry.

(I wouldn't use its BDD style syntax for unit tests. But that's just me, and it's a take-it-or-leave-it, so easy enough to opt-out.)

Collapse
 
jessekphillips profile image
Jesse Phillips

There are a number of things that I find I don't like about TDD.

TDD is not about tests or testing, it is about design. But I don't like the design it promotes. Actually I have mixed feelings. The main complaint is that it can cause what would be a straight forward implementation and convolute it with abstractions and boilerplate.

What is nice is that the code can become easily exercised without full system execution. However this is good when the design chosen is pure functions, but all too frequently I see layers of mock and dependency injection frameworks used to manage dependency creation rather than dependency reduction.

D is such a blast to create compile time testable code.

Collapse
 
danielfoxp2 profile image
danielfoxp2

TDD doesn't promote any design. You do. TDD, at best, can show you your design is flawed.

Collapse
 
jessekphillips profile image
Jesse Phillips

That is never anything I've heard.

"show you your design is flawed."

Isn't that what promotion would be in this context? How can it show a bad design if it does not expect some elements of design?

Thread Thread
 
danielfoxp2 profile image
danielfoxp2

You can do whatever design you want or your knowledge allows you to do. TDD will not stop you. It shows that you have problems by being fragile, not reliable, hard to maintain and understand.

If you need alot of unrelated boilerplate to exercise one behavior this is an indicator that your design can be problematic. But nothing will stop you to keep move forward with it.

So no, the design is entirely up to you.

Thread Thread
 
jessekphillips profile image
Jesse Phillips

I wasn't referring to boilerplate within the test. In order to write specific test scenarios it can require a set of interfaces or you need to use pure functions with PODs. You have to build the code such that it can be tested (in isolation) with code.

Thread Thread
 
danielfoxp2 profile image
danielfoxp2

There is another name for isolation: Low Coupling and High Cohesion. These are concepts of good design for ages.

But, again, TDD don't force you to follow them. You can do a very good spaghetti code using TDD and never look back, even feeling the pain related to it.

It's not TDD that promotes such design. It is the programming practice itself. The collective knowledge of our field that had discovered some really important and useful practices, patterns and principles to guide us in our journey.

But, again, if you really, really, reaally, want then nothing will stop you of not follow any of these practices, patterns and principles when doing TDD.

At the end of the day, you are the guy making decisions, creating architectures and solving problems (or amplifying them =p).

Thread Thread
 
jessekphillips profile image
Jesse Phillips

But TDD specifies that you are supposed to test one thing. It is not as the name suggest, put down any test in any way and then build. Following TDD is much more opinionated then you make it out to be.

Thread Thread
 
danielfoxp2 profile image
danielfoxp2

Yeah, you exercise one piece of the behavior, then move to the next one. I was not in that level of detail while stating my claims. I just assumed it was not needed. My bad.

Even exercising one piece at a time you can bloat you code and make it very hard to make specs for it, to make changes on it, etc. So in a nutshell and as an overview, I'm saying that you will do the design that you know and that can be bad or good. TDD doesn't care. It will not stop you from doing it. It is different from a MVC framework that states you need to make the controller name same as the view folder or something like that, this is opinionated. TDD is not like this. So, there is not a promotion to a certain kind of design.

This kind of design is promoted by our field itself. Not by TDD. This is my argument.

Hope I've made myself clear now as english is not my native language. Sorry for any mistakes and misunderstandings. Cheers.

Collapse
 
codemouse92 profile image
Jason C. McDonald

Even more, later in your great comment you state “there are practices that work for me“ which is IMSO the same biased attempt to invent a silver bullet—now among yourself during the time.

Except I don't have any silver bullets there. The exact practice varies from one project to the next. ;-)

Collapse
 
ns23 profile image
Nitesh Sawant

Can anyone please tell me how to follow a TDD when writing a rest API in express js ?

Collapse
 
wolverineks profile image
Kevin Sullivan

My Ruby is a bit rusty, but it looks like the tests are testing whether the method returns true or not. What would it look like to test that the side effects executed as intended?