Comments by "" (@grokitall) on "The Most Common Test Driven Development Mistakes" video.
-
legacy code almost by definition is code where testing is at best an afterthought, so retrofitting it to be testable is a pain.
luckily, you don't have too. not all code is created equal, so you write new code using tdd, and as part of the refactoring step, you move duplicate code out of the legacy section, modifying and writing just enough tests to make sure it continues to work.
this results in a code base where the amount of untestable code keeps reducing, while the code under test keeps increasing. more importantly, you only modify the legacy code when it needs changing. the rest stays the same.
working any other way is basically chasing code coverage for a system not designed to be tested, which is why dave says trying to force it under tdd style tests is a bad idea.
over time, more and more code needs modifying, and thus comes under test.
1
-
1
-
@georganatoly6646 this is where the ci and cd distinction comes in useful.
using c for illustrative purposes, you decide to write mylibrary. this gives you mylibrary.h which contains your public api, and mylibrary.c which contains your code which provides an implementation of that public api.
to the extent your tests break this separation, they become fragile and implementation dependant. this is usually very bad.
by implementing your unit and integration tests against the public api in mylibrary.h, you gain a number of benefits, including:
1, you can throw away mylibrary.c and replace it with a total rewrite, and the tests still work. to the extent it does not, you have either broken that separation, or you have not written the code to pass the test that failed.
2, you provide an executable specification of what you think the code should be doing. if the test then breaks, your change to mylibrary.c changed the behaviour of the code, breaking the specification. this lets you be the first one to find out if you do something wrong.
3, your suite of tests gives lots of useful examples of how to use the public api. this makes it easier for your users to figure out how to use the code, and provides you with detailed examples for when you write documentation.
finally, you use the code in myprogram.c, and you have only added the functions you need to the library (until someone else starts using it in theirprogram.c, where the two programs might each have extra functions the other does not need, which should be pushed down into the library when it becomes obvious that the code should be there instead of in the program).
you then use ci to compile and test the program, at which point you know that the code behaves as you understand it should.
this is then passed to cd, where further acceptance tests are run, which determine if what you understood the behaviour matches what your customer understood the behaviour to be. if there is a mismatch found, you add more acceptance tests until it is well enough documented, and go back and fix the code until it passes the acceptance tests as well.
at this point not only do you know that the code does what you expect it to do, but that this matches with what the customer expected it to do, in a way that immediately complains if you get a regression which causes any of the tests to fail.
in your example, you failed because you did not have a program being implemented to use the code, so it was only at the acceptance test point that it was determined that there were undocumented requirements.
1
-
1
-
1