
TDD on a 'Hello World' - fagnerbrack
http://cascadefaliure.vocumsineratio.com/2019/02/tdd-hello-world.html
======
gmiller123456
I'm sure everyone who's heard of TDD has played through similar scenarios in
their head. But even the most evangelized TDD proponents almost always end
their talks with addressing the fact that some code can't or shouldn't be
tested. And I think the fact that they wait until the end to say things like
that turns a lot of people off of TDD.

I have used TDD to develop libraries and services, and it works really well.
You have to write code to test your work anyway, so it's not really a lot of
extra work. And writing the tests first really helps (me) think more about how
the user will use my library/service.

I, in no way, think that _EVERYONE_ should _ALWAYS_ use it, in the way that a
lot of people have preached. But it is frequently a good tool for the job, and
I think a lot of people have abandoned the idea because of the way a few bad
actors have presented it.

------
mikekchar
I'll be frank. I hate arguments like this: reductio ad absurdum. However,
let's try and save this blog post. Why, exactly, have we run into a problem
trying to test drive "Hello, world"?

First, let's ask the question: what is the important output of "Hello, world"?
What are we trying to accomplish. Usually it is a program to see if the
compiler is working. It is, in itself, an acceptance test. We don't TDD
acceptance tests generally.

However, let's say that we want to write a program that outputs a string
literal to STDOUT for some reason. Is the string literal itself an important
part of the functioning of the program? If I change the literal, will the
program function badly? This actually gets to the heart of "unit test" versus
"acceptance test". With an acceptance test we want to write tests that match
our implementation with business requirements. However, with unit tests our
goals are much more humble. We wish to alert ourselves when modification to
the code has changed the behaviour of the program. We do not actually need to
assert that the behaviour matches the business requirements (that's what
acceptance tests are for!)

This solves one of the dilemmas in the original article: No, there is no need
to make "Hello, world" more general. And we do not actually need to test that
it outputs "Hello, world". Instead, we need to have an acceptance test (which
might be a manual test) that shows that it outputs "Hello, world" and we need
unit tests to show that changes to our implementation do not change behaviour.

In other words, for a unit test, we should concentrate on whether or not we
have written _something_ to the stream. We may even want to check to see if
that _something_ is the string literal (which we could put in a constant for
easy access). However, there is no need to assert that the string literal is,
in fact, "Hello, World". I really can't stress enough the importance of this
distinction in writing good unit tests.

So I need a library that has a string literal as a constant and a function
that takes a stream and prints the string literal to it. Then I need a test
that sets up the stream, runs the function and reads the contents of the
stream. This is not any more complicated than the problem deserves.

Do we need to test that main, run the code? I think this is a very interesting
question. Again, we need to go back to the difference in purpose between an
acceptance test and a unit test. If we can keep the code in main down to a
single line, then perhaps we can simply have an acceptance test (possibly
manual) that tests that the program starts up properly and unit tests for the
behaviour of the program. If main starts to have more than one behaviour
(startup), then we will have to refactor it.

What happens when we _do_ want the program to print something other than a
string literal? _That 's_ when we start adding complexity to the program.

If you find that you are adding unnecessary complexity to your program in
order to write unit tests, usually it's a good sign that you are approaching
your unit testing in a less than optimal fashion. There _are_ cases where it
is extremely tricky to write good unit tests (my favourite example is trying
to test a function that outputs an infinite series -- like the other favourite
"TDD doesn't work" example, Fizz Buzz).

I hope some people find this interesting/useful. Like I said, I generally hate
arguments like the one the article presents and I usually try not to respond
to them, but I was at a loose end tonight.

Edit: Nonsensical wording.

