Since I got addicted to unit tests more than 10 years ago, in the majority of my workplaces I was the first who has ever heard about these. Nevertheless I kept writing my little unit tests whenever I could, and estimating the cost of unit testing into my tasks. Whenever someone asked about my coding habits, I told what I was doing and why it worked for me. Usually at least some of the people were interested, and eventually I got to give presentations on the topic and mentored people to write their first unit tests.
You don't need to start convincing people about the agile way the first day at your new workplace. Just follow the principles in your own work as much as you can. If you do it well, you will deliver better code. If your coworkers and/or management notice it, they will ask how you do it. Then you can tell them.
Update
Most of the seasoned developers (and managers) have seen trends and fads come and go, so they do not get excited by the latest buzzwords. However, if you can demonstrate that a certain approach (tool, way of thinking) really works in practice, in the actual project, the ones who care about their craft will almost surely sit up and listen. But if you have no such people in your team, maybe it is time to look for a better place...
There are already lots of good answers to this question, and I've commented and upvoted several of them. Still, I'd like to add some thoughts.
Flexibility isn't for novices
The OP clearly states that he's not experienced with TDD, and I think a good answer must take that into account. In the terminology of the Dreyfus model of skill acquisition, he's probably a Novice. There's nothing wrong with being a novice - we are all novices when we start learning something new. However, what the Dreyfus model explains is that novices are characterized by
- rigid adherence to taught rules or plans
- no exercise of discretionary judgement
That's not a description of a personality deficiency, so there's no reason to be ashamed of that - it's a stage we all need to go through in order to learn something new.
This is also true for TDD.
While I agree with many of the other answers here that TDD doesn't have to be dogmatic, and that it can sometimes be more beneficial to work in an alternative way, that doesn't help anyone just starting out. How can you exercise discretionary judgement when you have no experience?
If a novice accepts the advice that sometimes it's OK not to do TDD, how can he or she determine when it's OK to skip doing TDD?
With no experience or guidance, the only thing a novice can do is to skip out of TDD every time it becomes too difficult. That's human nature, but not a good way to learn.
Listen to the tests
Skipping out of TDD any time it becomes hard is to miss out of one of the most important benefits of TDD. Tests provide early feedback about the API of the SUT. If the test is hard to write, it's an important sign that the SUT is hard to use.
This is the reason why one of the most important messages of GOOS is: listen to your tests!
In the case of this question, my first reaction when seeing the proposed API of the Yahtzee game, and the discussion about combinatorics that can be found on this page, was that this is important feedback about the API.
Does the API have to represent dice rolls as an ordered sequence of integers? To me, that smell of Primitive Obsession. That's why I was happy to see the answer from tallseth suggesting the introduction of a Roll
class. I think that's an excellent suggestion.
However, I think that some of the comments to that answer get it wrong. What TDD then suggests is that once you get the idea that a Roll
class would be a good idea, you suspend work on the original SUT and start working on TDD'ing the Roll
class.
While I agree that TDD is more aimed at the 'happy path' than it's aimed at comprehensive testing, it still helps to break the system down into manageable units. A Roll
class sounds like something you could TDD to completion much more easily.
Then, once the Roll
class is sufficiently evolved, would you go back to the original SUT and flesh it out in terms of Roll
inputs.
The suggestion of a Test Helper doesn't necessarily imply randomness - it's just a way to make the test more readable.
Another way to approach and model input in terms of Roll
instances would be to introduce a Test Data Builder.
Red/Green/Refactor is a three-stage process
While I agree with the general sentiment that (if you are sufficiently experienced in TDD), you don't need to stick to TDD rigorously, I think it's pretty poor advice in the case of a Yahtzee exercise. Although I don't know the details of the Yahtzee rules, I see no convincing argument here that you can't stick rigorously with the Red/Green/Refactor process and still arrive at a proper result.
What most people here seem to forget is the third stage of the Red/Green/Refactor process. First you write the test. Then you write the simplest implementation that passes all tests. Then you refactor.
It's here, in this third state, that you can bring all your professional skills to bear. This is where you are allowed to reflect on the code.
However, I think it's a cop-out to state that you should only "Write the simplest thing possible that isn't completely braindead and obviously incorrect that works". If you (think you) know enough about the implementation on beforehand, then everything short of the complete solution is going to be obviously incorrect. As far as advice goes, then, this is pretty useless to a novice.
What really should happen is that if you can make all tests pass with an obviously incorrect implementation, that's feedback that you should write another test.
It's surprising how often doing that leads you towards an entirely different implementation than the one you had in mind first. Sometimes, the alternative that grows like that may turn out to be better than your original plan.
Rigour is a learning tool
It makes a lot of sense to stick with rigorous processes like Red/Green/Refactor as long as one is learning. It forces the learner to gain experience with TDD not just when it's easy, but also when it's hard.
Only when you have mastered all the hard parts are you in a position to make an informed decision on when to deviate from the 'true' path. That's when you start forming your own path.
Best Answer
Basically yes. The tests are there to ensure the system has some property. Usually that the system provides some functionality. (We sometimes write test to reproduce a defective behavior, too, however, for instance when filing a bug report.) So if you write new tests that are less fragile because they are more decoupled from the production code, and also more expressive because they state what functionality the system achieves, and not how the system achieves it; the old direct tests don't provide any additional value, and should be removed because they are a maintenance burden. Indirection enables you to achieve both decoupling and expressiveness.
Uncle Bob's green diagram on the right would be and example of the facade pattern because it hides multiple, complicated services behind a single interface.
To clarify one thing for junior developers do not read Uncle Bob's UML diagram literally. Services implement the facade in a loose sense.
See below example to see how application services implement the test facade. Also to see the difference/relationship between and the application facade and the test facade (as per @jonrsharpe's comment). A tip can be: write your test facade such that it reads like the stories you will demo, or the scenarios in your test plan etc. :