Guillaume Chérel
Guillaume Chérel

Reputation: 1518

How to unit-test a function that uses other unit-tested functions?

How can you test a function that you've written using other functions that you have already tested? Say a functions f already has unit tests such that we're fairly confident it behaves as we expect. Here is a trivial example of another function g written in terms of f:

def g(a,b):
    return f(1 / b, a)

The only work that g does is some manipulation of its arguments and to pass them to f the right way (in a more complicated example we may have more elaborate manipulations of the arguments). Since f is tested already, the only thing I need to test with g is that I manipulated the arguments correctly and I didn't mix them up when passing them to f. I see two approaches:

  1. Classical way: Find pairs of (arguments, expected_results) by hand and write tests like g(1,2) == 3. With this approach I would implicitly re-test f since I would have to find by hand what result is expected for f(1/b, a). Also, it could be that computing f(1/b, a) is difficult to do by hand for any trivial b (say f was tested with easier values).
  2. Re-use f in the tests: write tests like assert g(1,2) == f(1/2, 1). But this looks a lot like rewriting the body of function g with the arguments replaced by actual values. I'd thus be likely to just make the same error twice, in the function body and in the test.

What's the best way to test g? Is there another way than these 2?

Upvotes: 2

Views: 261

Answers (2)

Vince
Vince

Reputation: 15146

How can you test a function that you've written using other functions that you have already tested?

Like any other function.

If f has been tested, then you shouldn't worry about it causing issues. If it does cause issues, you didn't properly test f.

I see two approaches

Go with the first approach.

You said "we may have more elaborate manipulations of the arguments", so the second approach is out of the question.

Not to mention, what if you accidentally mess up the rhs of the expression?

assert g(1, 2) == f(1 / 1, 2)

With this approach, I would implicitly re-test f

You aren't re-testing f, you are using a tested feature inside of your implementation.

That's only a problem if f is faulty. You wouldn't mind using behavior from the standard library in tests, so what makes f any different if you say it has been tested?

The only thing I need to test with g is that I manipulated the arguments correctly and I didn't mix them up when passing them to f

The second statement is a bit excessive. Mixing up the arguments usually shows when black-box testing: if g doesn't pass the arguments to f in the correct order, your results shouldn't be as expected/you should expect invalid results.

If your results are correct, yet the arguments are mixed up, then your assertions are not finely-grained and you should make them more detailed. Below is an example of poor testing. Although the assertions are correct, the two seem the same from a black-box sense (which they obviously aren't):

def mod(a,b):
    return a%b

assert mod(10, 4) == 2
assert mod(10, 9) == 1

def divide(a,b):
    return a/b

assert divide(10, 4) == 2
assert divide(10, 9) == 1

Mixing up arguments could be problematic, but it'll only hit production if your tests aren't detailed. If you have issues with g, you may want to decompose the mutation behavior:

def g(a,b):
    return f(mutateB(b), mutateA(a))

Then test the mutate functions independently, make sure they work.

Upvotes: 2

Adam Siemion
Adam Siemion

Reputation: 16029

Another approach would be to stub the f function, i.d. provide an alternative implementation of f which be used in tests, which responsibility is to return a defined value for provided input. This will allow to test g in insolation.

Upvotes: 1

Related Questions