javascriptunit-testingtestingtddmethodology

Can test be green from the very beginning in TDD?


Imagine I have function foo():

var foo = function(string) {
    return string.replace(/a/g, '');
};

I have following tests for it to work:

  1. foo() exists;
  2. foo() strips a's from string and does nothing to string without a's;
  3. foo() throws TypeError if given something else than string;

The problem is with test #3 — however it's green from the beginning, it's not my merit. I expect to have written something like this:

var foo = function(string) {
    if (typeof string !== 'string') {
        throw new TypeError('argument should be a string');
    }

    return string.replace(/a/g, '');
};

but I can't, because there is no test for it. So foo() really throws TypeError, but not because of argument of wrong type, but because null, undefined, number, array, boolean, regexp etc. objects given as argument don't provide replace() method.

I think I need this test, just because JS team may for example change TypeError for this particular case to something like MissingMethodError, but I will violate Red > Green > Refactor principle. How should I resolve this situation?


Solution

  • This can happen, but it's better to avoid it. In this case, you could have avoided it by writing your tests in a different order.

    1) foo exists;

    Red because of course foo doesn't exist. Write an empty foo(); now it's green.

    2) foo throws a TypeError if the argument is something other than string.

    Red because foo never throws a TypeError (or anything else). Have foo unconditionally throw a TypeError; now all tests are green.

    3) foo strips out the a's.

    Red because it doesn't. Now implement; now all your tests pass, and all of them started out red.

    It seems like a contrived approach, and of course it is, and it's not easy to know in advance how to write the tests to adhere to red-green-refactor. But the more you do it, the easier it becomes.

    Red-green-refactor really is important and worth trying to follow, because if a test starts out green, it may not be testing anything meaningful about the system under test. Sometimes, as in this example, you bump up against it; you can tweak the system under test to force a red, but also try to learn from it (as you are doing by posing this question) so it goes better next time.