0

I'm writing some tests for a NodeJS module, but this question can apply for any language / platform. I've got the following situation:

// in moduleA.js

exports.foo = function (var1, var2) {
    // do something to var1 and var2 here

    baz(var1, var2);
};

exports.bar = function (var1, var2) {
    // do something completely different to var1 and var2 here

    baz(var1, var2);
};

function baz(var1, var2) {
    // do something to var1 and var2 yet again that both foo and bar need

    moduleB.doSomething(var1, var2);
}

So, both foo and bar are public methods of moduleA. They've got some specific functionality (they do different things to var1 and var2) but also some shared functionality (to keep things DRY I've moved it into a private method, baz) and then they both call a public method on moduleB.

I've stubbed out moduleB so I know exactly what's going on after foo and bar have been called. I'm writing tests for foo and bar's specific functionality but I'm not 100% sure how to approach testing the shared functionality in baz.

It seems to me I've got three options:

  • write identical tests for both foo and bar that test for the things in baz
  • write a single test for foo and not write the same test for bar since know that baz works
  • expose baz as another public method for the module and test it separately.

I'm leaning towards the first option, but I feel I might be missing something.

  • 1
    You should write enough tests to give you confidence that your code works. If you're "driving in a low gear" (ie you're unsure of the correct implementation), or if high coverage is your goal, you might want to write identical tests for both foo and bar. If you're feeling confident, you might feel that a single test for "foo" is enough. Whatever you do, don't test baz directly - it's an implementation detail and you don't want to break tests when you refactor it. – Benjamin Hodgson Mar 6 '14 at 11:40
3

Unit tests shouldn't care about implementation details. Unit tests need to check that the the function returns the expected result, causes the expected side effects(and, if possible, not causing any other side effects), and throws the expected exceptions. How the functions does it should not concern the unit tests, just like it shouldn't concern the user of the module.

You don't need to unit-test baz(unless it's a module internal unit test) because it's not part of the module's API. That means that you should be able to remove it, rename it, or change it's functionality as long as the module's public functions still work, and neither the user code that uses the module nor the unit tests should change.

With this approach, it's easy to see that you should choose the first option - write unit tests for both foo and bar. The unit tests shouldn't care about baz, since you should be able to refactor it out of foo and/or bar, or to create a new version of it - baz2 - which bar will use while foo keep using original baz, or change some execution path of baz that neither foo nor bar should ever invoke. Those chances do not affect the module user, so they should not affect the unit test either.

If baz has some side-effects that foo activates, as far the the module user and the unit tests care those are side-effects of foo, so you should test them accordingly.

6

The key its to think in test for feature/behavior not in test for methods.

Its difficult without real names, but, "foo" exists to offer some behavior, the same with "bar", you simply need tests to check that the behavior of foo is correct and other to verify the behavior of "bar", when you discover that this two methods shared some internal functionality and decide to make a refactor your test remain the same, and thanks to that you can be sure that your refactor don't break any functionality.

I know that its probably generic advice, but its really difficult without a concrete example.

  • Thanks for your answer - went for Idan's since it went into a bit more detail. Also, apologies for not giving a more concrete example, the methods I was working on were very domain specific and it felt they would've only muddled things further. – Andrey Mar 12 '14 at 16:09
0

The test for moduleB.doSomething() doesn't belong into this test at all, except incidentally. It should be in the test for module B.

If baz were private to its module, and whatever it does is part of the public contract for several routines that use it, then its effects would have to be tested. As it is, the most you should do is query your B mock object whether baz was called, not whether it did its job.

  • baz is still inside moduleA. moduleB.doSomething() is stubbed out and not part of what I'll be testing for anyway. – Andrey Mar 6 '14 at 11:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.