Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: How do I start with test driven development?
136 points by pravj on Feb 21, 2015 | hide | past | web | favorite | 64 comments
I'm a student developer, @pravj on GitHub. I try to follow the best possible development practice when I'm working like documentation, respecting language's style guide etc.

But one thing I don't like with me is that I never write tests.

Why I never followed that aspect is because I always felt that the thing that I'm writing, sounds totally OK.

One another negative point is that I don't actually get it that How to do/start this and what type of tests I should write for any given project. Maybe this is because I have never done this before.

For example recently I wrote a identicons library, Penticons[1]. It generates GitHub contribution flavored identicons. You can read the development story here[2].

Now, the thing that stopped me from writing tests was, I thought that ultimately it's a image generation library, why this thing needs any tests.

I don't want to lost my 1 point in the Joel score. Whenever I see a build passing label on a GitHub repository, It makes me crazy.

I'm asking here because I just love HN, I know people will suggest some awesome things here.

1. https://github.com/penticons/penticons.go

2. http://pravj.github.io/blog/penticons-the-hash-game/




As far as writing tests... I'm sure you are testing your code manually yourself already, right? When you make a change, you do something(s) to make sure your change had the effect you wanted, and your software still worked. You just do it yourself, manually, and do different things every time of course.

Take those things, write automated tests to test them. Run the automated tests instead of testing manually, every time you can. Especially when making changes don't run the software yourself to see if your changes did what you wanted, write a test to do it and run that. The first time one of your tests catches a bug that you never would have expected, you'll be hooked.

"Test-Driven Development" actually refers to (almost) always writing the tests _before_ you write the code they test. Not everyone subscribes to this, at least universally. I wouldn't worry about that for now, I think if you just start writing automated tests every time you would have been manually testing, you'll be on the path and things will fall into place.


This is the point. You are most likely already testing your code (logging, printing, or the like).

I learned how to write tests and I was sold on the value of TDD only after I was in the middle of writing a non-trivial, multi-module client/server app. Right now, you think "I don't need to write tests for my penticons library, because I know it works." That's understandable. But as soon as you start incorporating it into other projects, or you extend it with other projects, having tests will make the process so much simpler.

Think about it this way: computing is essentially a way to repeatedly do things really quickly. I could take a pencil and a paper and write the numbers from 1-100 without too much trouble. But when that number is over 1000, or if instead of simply writing the number you perform some tedious formula, then you start to yearn for something like a `for` loop control structure.

The same with testing. Once you have a bunch of moving pieces, and every change you make you find yourself diving into the repl for 10 minutes doing the same thing over, and over, and over... you will actively WANT to know how to test.

So don't sweat it. Learn it if you want. Sooner or later, you're going to _need_ and _want_ to know how to write good tests, simply to save yourself the hassle.


I think Thoughtbot did a nice job of explaining TDD in this 1 hr video (playback speed was increased). Its part 1 or part 2 I forget.

My favorite part is when Josh Clayton excitedly said "Writing our tests is the fun part! Failed tests tell you what to do next!". It was a sort of an aha moment in Rails for me where everything came together.

Moreover, the emphasis was on integration testing (Capybara/Rspec), which I could more easily grok and see the importance, then say unit testing.

Here's a link to the Integration Testing Videos and I hope they could publish this for every one to see freely. I personally feel this was an eye opener to me, and could possibly be one for others as well.

https://upcase.com/test-driven-rails

P.S. I want Thoughtbot to make more real-world applicable quick-cut videos (e.g. Proper usage of the GH PR/Merge Model would be great). The weekly iteration is interesting, but its too conversational. I like rehearsed sentences that quickly convey the knowledge to me. That's the missing piece for me to subscribe.


I can't recommend the discussion between Kent Beck, David Hansson and Martin Fowler enough. They look at how and where it should be used and in particular discuss their approach to mocking which I feel is an often misunderstood tool.

Part 1 - https://www.youtube.com/watch?v=z9quxZsLcfo

Part 2 - https://www.youtube.com/watch?v=JoTB2mcjU7w

Part 3 - https://www.youtube.com/watch?v=YNw4baDz6WA

Part 4/5 - https://www.youtube.com/watch?v=gWD6REVeKW4


This was a great discussion, but I think it is not appropriate for a beginner just getting into TDD. There were so many nuances in this series that I don't think it would be helpful as an introductory resource.


Woot, this is an awesome-people-discussion. Thanks for it.


I can give you some pointers. 1) A good discussion about Unit Testing on Hacker News. Is most unit testing a waste or not? https://news.ycombinator.com/item?id=7353767

2) A story about unit testing at Airbnb: http://nerds.airbnb.com/testing-at-airbnb/

3) Practical tips for test driven development: http://soundsoftware.ac.uk/unit-testing-why-bother/


Reading the Airbnb story now, thanks for the links.


Sure, you're welcome. If you would like to grasp where you can go with test driven development and continuous integration, check out this article: 'How Etsy Deploys More Than 50 Times a Day": http://www.infoq.com/news/2014/03/etsy-deploy-50-times-a-day (Btw. I won't say that deploying 50 times a day is a best practice in all cases)


My favorite resource for learning how to test your code is http://getpython3.com/diveintopython3/unit-testing.html

Once you start testing your code, it's addictive. Testing gives you confidence.

- By forcing you to detail your requirements, writing tests helps you make sure that you're building the right thing.

- Testing lets you refactor your code with confidence -- if your old tests still pass after you refactor, your new version works the same way as your old version.

- Testing makes your code much (MUCH!) more maintainable. As long as your old tests still pass, you can be confident that the new code you wrote didn't break any of your old code.


Yeah, addictive it is.

I remember last year contributing[1] to a 2048 python game, where the maintainer committed[2] test suite lengthy than my commit and He was doing it like a charm.

I think I should start from that very repository as I know it very well already.

1. https://github.com/bfontaine/term2048/commits?author=pravj

2. https://github.com/bfontaine/term2048/commit/6f9a46283c2cc0a...


That looks good. For counterpoint, here is my 2048 including tests for just the tricky bit: https://github.com/darius/sturm/blob/master/2048.py

I have Emacs set up to run them on a keypress without even having to save the file; you can create and run these tests even quicker than trying things out in a REPL, and unlike the REPL they persist to be rerun after the next change.

If you've been feeling like testing is too much work, one angle is to reduce the amount of work it'll take to start.


I've been writing Python unit tests lately too. I stick all the tests in files named test_.....py, have a top level file run-tests.py that runs them all, and then (this on Ubuntu): watch -n 0.1 ./run-tests.py in a window on the side.

Then I can go off and change stuff and as soon as something breaks I see it in the side window. If I break something and the tests don't break, well, time to write more tests :-)


Start watching James Shore's "Let's Play: TDD" videos [1]. Yes, it is in Java and about making a basic desktop app for finance tracking - but that doesn't matter. I'm cutting out paradox of choice and just telling you to start here :)

This is a great introduction to the basics of TDD, is appropriate for beginners, shows trial and error (not edited/rehearsed 'final versions'). It is very digestible (15 min per episode) and free to watch on YouTube. It will seem tedious at times, but just commit to watching the first couple episodes and stick with it.

Destroy All Software, "Is TDD Dead" Hangouts, other more specific books, etc are all amazing resources - but they are not a good place for a beginner to start. Once you have a basic understanding and have personally run into some of the tricky situations (mocks/stubs, testing at system boundaries, slow test suites, etc) these are invaluable, but until then just file them away to revisit later.

[1]: http://www.jamesshore.com/Blog/Lets-Play/Lets-Play-Test-Driv...


Thanks for the plug. :-) I also have a JavaScript TDD screencast at http://www.letscodejavascript.com. Different technologies and challenges, same concept.


I've found the Craftsman series by Uncle Bob[1] to be very helpful in showing how to do not only TDD, but also pair programming. Before that I would complete tutorials and exercises, but was never able to effectively use it in actual projects. The Craftsman series shows the steps of using TDD in both easy and tricky situations, such as socket code, and enabled me to become much more comfortable with the process.

[1] http://www.objectmentor.com/resources/publishedArticles.html


I was just about to post this! Craftsman was the the thing that made me actually grok TDD.


I got into it by watching DAS (destroyallsoftware.com) by Gary Bernhardt (creator of the WAT presentation), it has truly shaped me into a better developer. Not only will you learn how to practice TDD, but so much more: refactoring, unix tools, OO-principles ... I could go on forever, it's really a great series.


Hi, currently writing a book on the subject and I'm doing a bit of research myself.

There are two aspects of testing that you need to discern: the development and the project management point of view. The first regards what and how to test things. This can be achieved by any developer and is moderately easy with the right help and mentoring. The second instead is the most difficult: trying to force tests in a non-test-focused environment, where the business does not understand the meaning and/or the value of testing can be poisonous to us and the business itself. In other words there must be an effort in both directions. I can keep talking about these two points, but I'll skip them for now unless you're interested.

Regardless of the current environment you're in, and taking for granted that the second part is - if not fully, at least partially satisfied, let's have a look at how to start with TDD: the major problem you're facing is that TDD does not define "what to test". That's why BDD was introduced, to fill in the gaps that TDD has.

Still BDD is somewhat lacking depth and proactive understanding from the project level on how much should be spent on unit tests, how much on functional and acceptance tests. On this note, Google has defined the ACC, which helps you define the testing plan for your application; I think it's totally worth from both a developer and business perspective to dig a bit more into that, especially if you want to get into testing properly.

If you still fail to see why tests are important, let's put it down this way, using James Whittaker's words: "although it is true that quality cannot be tested in, it's equally evident that without testing is impossible to develop anything of quality".

my 2 cents


"Hi, currently writing a book on the subject and I'm doing a bit of research myself."

"trying to force tests in a non-test-focused environment, where the business does not understand the meaning and/or the value of testing can be poisonous to us and the business itself"

Sorry to be a bit negative, bit it sound like you have a conclusion that you want to reach before you have done your research. That's not really how research should work. Research should be done and conclusions reached afterwards.


TDD is a subset of automated testing as a whole. You'll find proponents of testing who might not actually like TDD. It's more of an opinion about the best approach. Anyhow TDD is essentially writing the APIs, interfaces, and function signatures first, then writing tests, then implementing things under the hood, then getting tests to pass.

Automated testing (unit or integration tests) is an extension of taking those little one-off things we do during development to make sure things work, and formalizing them to be a permanent part of the code base. Some people go further then and try to add tests for every feature or (gasp) every single line and sub-line of code.

It's easy to get carried away with testing. And some people have valid arguments against testing. But it's nothing too hard or unapproachable. The point really is that everyone does testing already, but not everyone keeps all their tests and makes them nice.


Case in point: I'm big on testing (I have pushed testing hard at three companies I have been at), but I dislike TDD strongly. I have often found that I am not sure how I would like to design components until I start implementing, and TDD is too rigid for arriving at an optimal implementation. I prefer to figure out the implementation, then write a thorough set of tests.


> You'll find proponents of testing who might not actually like TDD.

One alternative I've heard about is bug-driven testing. Whenever the code doesn't work as expected, write a test verifying the desired behavior. The reasoning was that the functionality was clearly fragile or complex, and therefore more likely to be accidentally broken in the future.


The reason you _need_ tests is so that (a) when you change some code in the future it doesn't break any of your functionality without you knowing about it and (b) if someone else changes you code it doesn't break any of your functionality without you knowing about it.

If you don't believe that you need unit tests, you won't write them - so step one is persuading yourself that they're useful. So try to remember a time when you broke your code in an unexpected way, and then try to work out what kind of unit test would have helped avoid that.


Well the question looks more like a why instead of a how.. answering the how is simple, if you are starting on a new project (as you seem to be), start with unit tests for all functions, for anything that talks to DBs/external APIs use mocks.

Answering the why,

scenario 0 - it forces you to have functions that do one thing and one thing well. Unit testing mostly helps people keep functions small and focused.

scenario 1 - assume you are working on a fun app without tests. Everything is fine and the app works great. After some time you decide to add a new feature X, and you realize you need to modify an existing function to achieve that.. you modify that, and the new feature X works fine, but two days later you will realize that an old feature Y is broken.

scenario 2 - scenario 1 but with collaborators instead of yourself revisiting the app.

scenario 3 - people use unit tests to understand code - while reading/working on other people's code. Unit tests also serve as excellent examples on how to call/use something, which params to pass etc.

There are a lot more reasons esp for large projects, and you never know when a small one off project grows into something big and critical so its recommended to always start with unit tests as adding them later is usually 10x more work as it would require refactoring etc.


Testing and certainly TDD usually fall by the wayside because it takes too long. At least that is the perception. I see two keys to keep tests lightweight, fast, and desirable.

First, you should choose testing and mocking libraries and learn them completely and totally. They are usually not that complicated. Read all the docs and memorize all the neat things they can do. Most libraries have a best practices guide. Read it and follow it. The point of this is to make sure that you can write tests as quickly as you can write trivial if/then statements.

Second, keep your tests as simple as possible while still testing behavior. Do not verify that every variable is set or if every method is called, or the right thing is passed everywhere. The tests should read like documentation: it should do x, it should do y. If you find the process of writing tests arduous or difficult to maintain, you're overcomplicating your tests (and probably your code, too).

In a word: lightweight.


In general, don't worry (too much) about tests in the beginning of a project until it's likely you'll have more than one user (aka yourself) and/or paying customers. If you have neither users nor customers, don't feel too guilty about your lack of tests. In many cases, it's more important to make something useful enough that makes an investment in more testing later worthwhile. Later, when you do have users and customers, you will want your tests to discover regressions before your users do.

However, the longer you wait, the higher the risk that you'll discover your code is not easily testable... which will require lots of refactoring to make testable. And that refactoring is made harder by the lack of tests...

In the meantime, if you're truly stuck, write down a list of how you're currently manually testing your code and write down edge and corner cases you're worried about.

(disclosure: I make testing tools for a living.)


In August of this year I walked into a job interview for an entry-level'ish Rails dev position at a start-up. Already having a great job in education, I made clear that I was weak in testing and needed a plan to patch things up. I'm now very confident with testing using MiniTest. Here're my 2 cents:

- Build something super easy (<4 hours), using Test-Driven Development. The example I used was a URL shortener.

- Rebuild one of your existing apps using Test-Driven Development. If you're wondering where to get started, watch videos titled "creating a simple app using ____ and TDD", or even consider calling the Rails hotline.

- Get someone to look at your test code for you. I learned a ton from this, I can't even begin.

- Use MiniTest. RSpec is amazing, but I found MiniTest to be more transparent as to what was actually happening.

- Check out the codeschool and codeacademy tutorials. Very helpful.

Best of luck. It's a very difficult topic, one that I really struggled with.


I would like to point out that TDD isn't really about tests, it's about design of your program. It forces you from the very beginning to deal with really important questions: how do one use a piece of your program? Is it easy, clear and simple? Can it be tested? What do you expect from the program? Is your program or component overcomplicated?

Design your programs from top to bottom fleshing out details as you go. Deal with one abstraction at a time. And when you're finished you can throw away non-critical tests so that they don't outlive their purpose.

Do remember that tests are code too and that they have to be maintained and that they can become a part of technical debt. Do not use TDD for prototypes when you really don't know how or why implement your program. Create a prototype, explore possibilities, throw the prototype away and write a production version with TDD.


A lot of the time (certainly in my work) the requirements are incomplete / vague enough, that it is easier just to build a prototype and work from there. As such tests will just require two or three times as much code, and there is a fair likely hood that they will be thrown way afterwards.

I know this does not fit waterfall or TTD / agile methodology, but I am certain that there is a very large percentage of IT workers that have to work this way.


TDD without refactoring is like sex without orgasm. Okay, maybe it's not that bad.. ;)

Seriously, the most fun I ever had with TDD was when using a language that my IDE had great refactoring tools for. Just write all your code inside a test to start with, don't bother with production anything. The test becomes a little like a command line program, launches immediately from the IDE and you can iterate super quickly. Your output is whether the test (with the test data) passes or fails. I'd pick the test framework by whatever the IDE liked best. Pursue solutions that viscerally satisfy until you know better.

Once you have a test working like that, do "Extract Method" followed immediately by "Move Method". The destination of the move is the production class. With two menu selections, you have a perfectly testable interface, called perfectly by the test. A good extract refactoring always proposes parameters for the method and lets you order and rename them. It will also recognize extractions that can't work, like when two results are generated by the selected code. In that case, extract more than one method and move all the methods to the production code.

Want to add more code? Do the same thing again and again and again, each time as a new test.

When you are done, you'll have production classes that are the composite of the extractions and all the tests that once contained inline code (but now contain method calls to the production code).

At some point, you will ask "how can I improve my tests?" The answer is easy: Code coverage tools. You should shoot for > 80% of your production code having test coverage. Some say more, few say less. Make some rules for yourself about things you don't bother testing, like getters and setters, use the 20% leeway so you aren't flogging yourself. Keep having fun, don't make a death march out of testing every last line of code.

This habit becomes really fun. You'll start telling your friends. Nirvana will ensue.


What IDE did you use? Can this workflow work in Visual Studio using NUnit?


ReSharper is an add-on component for Visual Studio that is (IMO) the best refactoring tool available for any IDE. ReSharper includes an NUnit-based test runner, too.

JetBrains is the company that makes ReSharper. I also use AppCode (their alternative to Xcode) in a way that's similar to how I use ReSharper. I do most of my iOS development in Xcode but load the project in AppCode before committing so I can take advantage of its superior code inspection and refactorings.


So TDD is a difficult thing to get into from the beginning. The more experienced you are as a developer the easier it will be to write tests. The thing that is great about TDD is that it forces you to write discrete and modular code as it tends to be easier to test that way.

This week, one of my coworkers described being new to TDD like being new to vim. There is a horribly steep learning curve but once you "get it" it becomes second nature. The real win of TDD, especially for open source, is that when the projects start to get bigger and harder to manually test you can merge PRs with a bit more confidence that you're not breaking things. I can speak with a bit of experience here as an OSS co-maintainer of two pretty large (>500 unique contributors) projects, graphite, and salt.


TDD takes time and is hard to get right at first. I'm sure you will find the usual tutorials so I would rather recommend this blog post: https://www.stevefenton.co.uk/Content/Blog/Date/201305/Blog/...

and Ian Cooper's talk "TDD, where did it all go wrong": https://vimeo.com/68375232

In a nutshell: test your high level API and not your implementation, minimize your usage of mocking.


For .Net developers I can recommend the combination of local dev machine continuous testing using NCrunch, manual runs using the unit test runner in Resharper and then the NUnit test runner in TeamCity.

TeamCity is a gatekeeper. Builds that fail unit testing don't get automatically deployed. Locally NCrunch keeps you updated on what is broken without you having to think about running your tests, since it runs them in the background for you. Resharper unit test runner helps you run and debug specific tests or groups of tests.

I work on a large application with many other developers and this flow works well for us.


James Shore's TDDJS series is supposed to be excellent: http://www.letscodejavascript.com/


I usually start with an "it works" test that calls each method with expected inputs and asserts an expected result. The expected result most of the time is that it didn't throw an exception. I found that such tests usually cover most of each method and are pretty effective at detecting regressions.

When I'm fixing a bug, I first create a test that fails when reproducing the conditions that cause it, then I make necessary changes until that test passes.


Thinking about testing can help with good overall design. Consider:

- How can I build a small, testable unit that can be combined with other small, testable units?

- How can I design a small, testable unit with a stable interface?

- How can I test this small testable unit in isolation and in various production/deployment scenarios?

Choosing the right building blocks to focus on meshes well with testing thinking b/c it lends itself to a bottom-up philosophy.


The best introduction to TDD that I've seen is Jon Reid's video tutorial here...

http://qualitycoding.org/objective-c-tdd/

Even though he's demonstrating TDD with Objective-C, I've recommended this video to .NET developers and they've still had an "Aha!" moment when Jon explains the "TDD Waltz".


> ultimately it's a image generation library, why this thing needs any tests

So you can be confident that it generates correct images, even three years from now when you've mostly forgotten about it but you dip back in just to add a quick feature without thinking about it very much -- or, for that matter, when someone submits a pull request, and you want to make sure it doesn't break anything.


One very easy thing to test for is wether your SVG creating code actually returns valid XML/SVG or not.

Then you might want to make some of these magic values configurable so users can create different sizes or different color schemes and some of them might be dependent. Unfortunately, I doubt that this refactoring is done without mistakes. Maybe you should write some tests before and then refactor.


TDD in what language? It's all about workflow. So if you're using java, you should get familiar with mvn and the junit lib. If you're using NodeJS, then you need to do something like mocha and grunt.

TDD gives you insane test coverage, but your process becomes: 1) Write a failing test 2) Write some code 3) Run test 4) Repeat 2 & 3 until test works, then go back to 1.


Long time ago I also didn't write tests. Now this software causes me the most problems. For start you shouldn't focus on testing with any methodology like TDD, BDD, etc., just ignore it. Better read about Right-BICEP, this going to help you shift way of thinking. Anyway after some hellish struggling you should be able always to think out tests before code.


>Now, the thing that stopped me from writing tests was, I thought that ultimately it's a image generation library, why this thing needs any tests.

I'm not sure this one does. It's a very simple library and there's no bugs in the issue tracker.

I would write a test for the first bug you encounter, though.

Or, if you start writing a more complex method on a new feature, start TDD'ing that.


There were two things that helped me get started: pairing with a more experienced developer who did TDD, and reading Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce. You might not have the luxury of the first, but the second is pretty accessible, even if you don't use the particular tools that the authors demonstrate.


I thought TDD seemed like a waste until I wanted to really figure out Rails and went through mhartl's Rails tutorial. By the end of the book, I realized that TDD had automated all the silly manual tests I did, and actually saved me time. I was kind of shocked, though thats what I get for thinking I actually knew an answer...:)


Really, you start by starting.

First grab a testing framework for your language, if it's not built-in. Something that runs the tests in your project once you run a command.

Following that, add a test to a function in your project. You can start with an easy one. Try that and see what works, try covering corner cases, etc

Then you can go for actual TDD once you write new code.


Tests for Penticons sounds straightforward to be honest. After saving some example images generated from a set of inputs, write some code that checks those inputs produce those images. Imagemagick has image comparison commands that can help . I did something similar when optimising a gaussian blur filter and found it useful.


I personally got started with Gary Bernhardt's excellent Destroy All Software screencasts. I started with the third season for his series on untested code, and got the other four soon after.

https://www.destroyallsoftware.com


Personally I love Uncle Bob's videos. He has some great TDD theory and work throughs - it is how I got started: https://cleancoders.com/

Specifically episodes 6 and 7. But if you have time - I recommend most of the videos


Writing tests is like writing articles. You just have to start writing and at some point you will get better.

Writing tests will change your coding style and way of thinking which is great. You might read some articles about testing for your language of choice.

Just start :)


When you're writing your procedures, you have to think about what you want them to do. What small unit of work each procedure should perform. That's the best time to write the tests for the procedure, because it will help you to think of edge cases that you might otherwise miss, and it will confirm that the procedure does what you expect before you move onto the next. It's also the easiest time to write the test for the procedure, because you haven't yet committed to any particular level of complexity or dependency structure. If you write tests first, your code will always be testable. If you attempt to show up the next day and start writing tests, you may find that the person who wrote the code yesterday wrote some pretty untestable code. Another benefit to having the tests is that they will continue to pay off as you modify the code weeks and months later, always confirming that you haven't broken anything.

Like most other learning endeavours, I found that the best approach was to start small. Try something simple like a fizz-buzz test. You'll probably have more trouble setting up and writing the tests than writing the code under test. That's okay. Like any other learning process, it takes time to get used to the new language and behaviour that is required to become fluent in writing testable code. Make sure that every time you save your working code, your tests run and you can see the result so that you get immediate feedback. And make sure to always work in as small a unit of work as seems comfortable.

Here are some example test function names for fizzbuzz

1. itPrintsFizzForMultiplesOfThree() 2. itPrintsBuzzForMultiplesOfFive()

These tests might be too broad in scope. Where is it getting the numbers that it's checking for multiples? What does "print" mean? How are the tests going to confirm what was printed?

1. itReturnsTrueForMultiplesOfThree() 2. itReturnsTrueForMultiplesOfFive() 3. itReturnsEitherFizzOrBuzzOrTheNumber() 4. itSendsTheResultToOutput()

These tests are against simpler methods and are easier to write but the tradeoff is that they make the code under test too fine grained at times. Can you have too many tests? I don't think that's likely in practice, not if they're true unit tests and extremely quick to execute. But some would disagree when they see a five method fizzbuzz class rather than a class with one "doTheWholeThing" method. You have to find what works for you. When you return to your code a day or two later, do the tests immediately make sense? If not, they're probably too complicated. Start small, get a feel for the shift in mindset, grow from there.


Here's the thing with TDD: until you work on a project that's either

a) contributed to by a lot of developers that aren't familiar with the source code,

b) so large that you're scared of making any substantial changes because the last time you did that another piece broke and it took forever to figure out it was actually broken and another forever to figure out how to get both pieces working together and in the process you half-broke this other piece and GAH maybe I should just rewrite the entire thing, or:

c) mission-critical, where a piece not doing what it's supposed to means lost lives or money,

then TDD is a net waste of time. Much like learning git, you'll be fighting an uphill battle with your mind at every stumbling block ("ugh this is so complicated - why am I learning this again? Doesn't putting my source code in Dropbox solve all of these problems already?") until you have an actual, real, legitimate use for the solution ("oh right, I'm learning git because using Dropbox to collaborate means 'conflicted copy' city").

Tests don't have to be complicated. They don't have to use the next new shiny framework, they don't have to have a smart lookup system that auto-runs files that are checked in and sends you an email if you push bad code; tests are just if statements, and they're mostly the if statements that you're already running through your mind when you program. Here's a hopefully not too contrived example:

When you wrote commit 4a069[1], how did you know the code you wrote actually did anything? From my quick understanding of reading this over, it looks like you changed `padding` and `size` from constants (5 and 30 respectively) to variables set somewhere else (in `utils.go`). If you made all these changes at once, then re-ran whatever code you use to make a penticon, the expected behavior is that you'd see the exact same thing as before (test#1).

But wait, were you even editing the same file you ran? Let's double `Padding` and `Size` in `utils.go` and make sure it changes the image (test#2). Alright cool, it does. But wait, the padding looks way too big - is it twice what it needs to be? Let's try `Padding = 1` and `Padding = 0` and make sure that looks how I expect it to (test#3, test#4). Awesome - that's perfect.

Does the size work too? I wonder if this would break if the `Size=0` (test#5). Oh shoot, what if both the size and padding were 0 (test#6). OH SHOOT WHAT IF THEY WERE NEGATIVE? (test#7).

---

All of that to illustrate: the difference between what you're (probably) doing right now and TDD is that in TDD you write down each test as you're testing it. At the end of adding those two variables you'd have a second file of 7 if statements that make sure that changing the size and padding works like it's supposed to.

Unfortunately, `penticons.go` is a horrible project to bring TDD into your workflow because 1) it's tiny and can probably fit in your head all at once so you'll know immediately anyway if something breaks, and 2) testing images is hard. It looks like a lot of your other public projects are equally as TDD-unnecessary.

What you could do is generate a bunch of references images (like a 1x1px empty image for when the padding=1 and Size=0), then write a script that runs the code that should generate the same image and make sure they match bit-for-bit. Again, though: until you're feeling the pain that having the piece of mind that no other part of your program was just broken by what you did, you don't need to add TDD to your workflow.

1. https://github.com/penticons/penticons.go/commit/4a069619545...


Thanks for the interesting perspective. I've tended to be rather skeptical of the strict TDD every-project-must-have-100%-test-coverage mindset myself.

For bringing some value back into automated testing, one guy I worked with thought that usually the tests themselves aren't that important, but instead what was beneficial about an automated testing mindset is that it forces you to architect your code well, into small, modular pieces with carefully specified cross-dependencies.

I will also say that I've found Git to be very beneficial for some types of hobby projects. Particularly the kind where you end up deploying the result in many places. Like if you're toying around with an Android app, it's nice to know exactly what version of the app is on this emulator, that emulator, your main phone, your tablet, your spare test phone, etc. That's why I find it useful to maintain a Git repo for it, carefully and religiously commit every new feature to it, and create a build system that tags the built files with the current Git commit somehow.


Wow, nicely explained. Thanks for your time. :)


This is almost like saying until you've been injured in a car accident there's no reason to wear your seat belt.


I'd argue that it's closer analogous to wearing a regular seat belt until you start driving in situations where the benefits of wearing a five-point harness outweigh the cons of reduced driving mobility.


isnt TDD dead already ?

Just write unit and integration tests


Still feeds the trainers and book-writes and as such is "the-only-correct-way-to-write-correct-software".


Have you tried Typemock? It helps you get started with unit testing and offers a free version


I'd recommend learning a new language that has a strong TDD community. I started testing and TDD by adding unit tests to an existing C++ project and didn't really get the point. I recently picked up Ruby and the course I took taught RSpec alongside Ruby. It made so much more sense. Plus, learn another language.


You start with a test (that fails).


TDD requires you write the test first, then the code later. At an extreme you would write a test for a minimal piece of functionality, then do the minimal amount of coding to get the test to pass and then repeat. I.e. you would write no production code that is not covered by a test.

This takes a lot of discipline and I have never done it a t work but it is fun to try at home to see what the extreme case is like. I don't do this at work because I have no control over 99.99% of the production code and how loosely coupled it is for testing. (In C#, but in Ruby you can get away with it due to duck typing).

TDD forces you to write code that is testable. So you never should end up thinking 'oh shit I need to expose that private member variable to make this class testable'.

Anyway just try it and then as you get stuck read up on how to do TDD. I am always an advocate of try before you learn (makes the learning easier).




Registration is open for Startup School 2019. Classes start July 22nd.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: