Journey Into TDD: I have no idea what I'm doing
There is something small and tiny that I feel I am missing about Test Driven Development. I understand the basic principle of the matter; write a test that fails, then write the code to make it pass, rinse and repeat; red, green, then refactor. Knowing that basic truth of what TDD is has not translated into actually being able to use it as a practice in my day to day coding.
What is it that I know about TDD, and how is that different from what I imagine I should know about TDD?
Test Driven Development is a way, a practice, to use a test first style of coding to help shape the structure of the production code; creating better separation of concerns (which limits strong coupling), a more logical organization of code, and all around better code. Whatever it means to be better code.
The test suite that exist after a TDD session is a mere side affect with the benefit of being able to detect regressions. The tests itself is not supposed to prove correctness of the production code, for it most likely does not cover every single possible case. Though it does offer some comfort when making changes to the code base later.
This is what I understand so far in my TDD journey.
Outside in, inside out
My experience with TDD is an odd one. It feels like I am doing it wrong.
For the sake of a concrete examples let’s look at a code challenge on Reddit’s DailyProgrammer; 355 Alphabet Cipher. This challenge takes a message and a short key. The key is expanded to the length of the message. Then a letter from the message and a letter from the key are used with a lookup table to return a character. This is done for every letter in the message which results in a secret message.
I ponder on my problem space and think of a place to start coding. I know I am going to want a function that takes a message and a key, then returns a string of the encoded message. Let’s call this Encode()
.
My traditional process is to work inwards. I decide on a point to start, what does my function do, and then I break it down into smaller steps that are needed to fulfill the function. Before I can encode my message with the key I need a way to get the letter from the key that would correspond to the position in the message. This requires have a string that is a repetition of the key and the same length as the message; the last key may be truncated so that makes the length the same.
The next thing I would need is a way to lookup the two letters on the cipher table; these would be the current letter being encoded from the message and the corresponding letter from the key.
Then I would iterate over each letter in the message, look up the corresponding letter from the key, lookup the conversion on the cipher table, collect each encoded letter and return them when I am done iterating over them.
This is straightforward, but I run into trouble when I try this process as TDD, and this is where I feel I am not understanding something. My first test is on the Encode()
function, but this will always fail until I write the many pieces that make up the internals of this function.
One thing that is said about TDD is that you should write as little code as possible to make the test pass. I take this almost literally, as I have seen examples where people have used return true
to get to Green. With this thought in my head I feel that the internals of the Encode()
function are too large. If writing the test first is supposed to shape the structure of the code then I do not see this happening in my situation.
The tests I write test Encode()
, but to make it pass I have to write a lot of code that is not seen by the test. At least it feels that way. It is in this way that I feel I am doing something incorrect, or there is some knowledge I have yet to absorb.
I wonder if I should be writing my functions inside out. If instead of starting with the test for Encode()
if I should be starting with a test the covers just one of the large chunks of functionality inside Encode()
.
If I write a test to find the character in the key that positionally corresponds to the message what would that look like? Would that be small enough to me to feel comfortable. Would I write that code and quickly get that Red-Green-Refactor cycle that other’s have talked about.
This might be the path.
The next step would be to write the look up in the cipher table given two letters. This also feels like it would be a smaller bit of code that could better embody the TDD philosophy.
After all the internal pieces are done then the actual Encode()
function would call the internal two functions, and use a loop to iterate over the whole of the message.
This seems good, but one thing that troubles me is how this would translate to larger projects. The example is a pretty straightforward code example, there is very little overall complexity in the architecture of the problem space. How would inside out testing work with a very large program? How deep would I have to go before the size of the code felt good for a Red-Green-Refactor cycle?
Testing with Go
My current experience could have something to do with my current language of choice, Go.
Go is a static language. It requires a little more upfront scaffolding at times to define types and structure. Could it be that these building blocks add a heaviness that I feel. Or do they distract from the flow of Red-Green-Refactor.
The flow and philosophy
I have had a hard time finding discussion on the spiritual internals of TDD. Almost every tutorial, discussion, or presentation focuses in on Red-Green-Refactor; write the smallest code to past the test and move on. It makes me wonder if I am overthinking this. I am I looking for something that doesn’t exist, and is not part of the practice that is TDD. Have I made this so difficult for myself without really needing to, or have I just started learning about testing in the wrong place.
This is my journey into Test Driven Development; an attempt for myself to see into the soul of it by using deliberate practice. To break apart the practice in order to fully understand it, or at the least to feel comfortable using it in my projects.