Contents

Contents

No True TDD Professional!

Contents

Someone on LinkedIn is complaining that this:

No True TDD Professional

…is not something a “true TDD professional would do”.

Well… yes and no :)

That is, indeed, a perfect green-phase code for the changes required by the exercise.

I would start exactly with a test like that:

python

class Test(TestCase):
    def test_encode_run_lengths(self):
        assert (encode_run_lengths("aaaabbbcca") ==
                [("a", 4), ("b", 3), ("c", 2), ("a", 1)])

It would exercise an empty function so it would fail (red phase):

python

def encode_run_lengths(s):
    pass

Then I would do exactly what the person in that screenshot suggests:

python

def encode_run_lengths(s):
    return [("a", 4), ("b", 3), ("c", 2), ("a", 1)]) 

And that point, I would stop and think about what I really need to solve the exercise.

All I need is a function that takes an input string and a start index, and returns the longest sequence of the same character, as well as the last index where it stopped.

So let’s write a test for it:

python

class Test(TestCase):
    def test_encode_run_length(self):
        assert encode_run_length("aaaaa",0) == ("a", 5, 5)

The code, at this point, is just:

python

def encode_run_length(s, start_index):
    pass

The test will fail.

Let’s make the test pass:

python

def encode_run_length(s, start_index):
    return "a", 5, 5

We can now refactor:

python

def encode_run_length(s, start_index):
    i = start_index
    while i < len(s):
        if i == start_index or s[i] == s[i-1]:
            i += 1
        else:
            break
    return s[start_index], i-start_index, i

Note that I’m not interested in creating optimal code at this stage nor evaluate all edge cases. I just want to implement the granular behaviour I’m laser-focusing on right now.

The test is now passing.

We can now add more cases:

python

class Test(TestCase):
    def test_encode_run_length(self):
        assert encode_run_length("aaaaa",0) == ("a", 5, 5)
        assert encode_run_length("aaaaabbb",5) == ("b", 3, 8)
        assert encode_run_length("aaaaa",4) == ("a", 1, 5)
        assert encode_run_length("a", 0) == ("a", 1, 1)

How about edge/invalid cases though? If we add them:

python

  assert not encode_run_length("aaaaa", 5)
  assert not encode_run_length("aaaaa", -1)
  assert not encode_run_length("", 0)
  assert not encode_run_length(None, 0)

The test fails! We did not consider them?

Let’s guard against those scenarios:

python

def encode_run_length(s, start_index):
    if s is None or not (0 <= start_index < len(s)):
        return None
...

Now the test passes.

Now that we are confident our building block works, let’s go back to the initial green phase and refactor that code:

python

def encode_run_lengths(s):
    groups = []
    index = 0
    while True:
       group = encode_run_length(s, index:q)
       if not group:
          break
       groups.append((group[0], group[1]))
       index = group[2]
    return groups

Again, I’m not interested in having huge quality code at this stage. I just want to validate the behaviour.

The test passes!

I can now refactor the code (maybe with a little help of AI) for a bit of elegance:

python

def encode_run_length(s, start_index):
    if not (0 <= start_index < len(s)):
        return None

    char = s[start_index]
    count = 1
    i = start_index + 1

    while i < len(s) and s[i] == char:
        count += 1
        i += 1

    return char, count, i

def encode_run_lengths(s):
    groups = []
    index = 0

    while index < len(s):
        result = encode_run_length(s, index)
        if result is None:
            break
        char, count, next_index = result
        groups.append((char, count))
        index = next_index

    return groups

We obviously want to check that the test still pass–and they do!

So, yes, a TDD professional would use the code in the screenshot, but maybe no true TDD professional? :)