Is code duplication allowed in this instance?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Jul 3 15:11:00 CEST 2009


On Fri, 03 Jul 2009 03:46:32 -0700, Klone wrote:

> Hi all. I believe in programming there is a common consensus to avoid
> code duplication, I suppose such terms like 'DRY' are meant to back this
> idea. Anyways, I'm working on a little project and I'm using TDD (still
> trying to get a hang of the process) and am trying to test the
> functionality within a method. Whoever it so happens to verify the
> output from the method I have to employ the same algorithm within the
> method to do the verification since there is no way I can determine the
> output before hand.
> 
> So in this scenario is it OK to duplicate the algorithm to be tested
> within the test codes or refactor the method such that it can be used
> within test codes to verify itself(??).

Neither -- that's essentially a pointless test. The only way to 
*correctly* test a function is to compare the result of that function to 
an *independent* test. If you test a function against itself, of course 
it will always pass:

def plus_one(x):
    """Return x plus 1."""
    return x-1  # Oops, a bug.

# Test it is correct:
assert plus_one(5) == plus_one(5)


The only general advice I can give is:

(1) Think very hard about finding an alternative algorithm to calculate 
the same result. There usually will be one.

(2) If there's not, at least come up with an alternative implementation. 
It doesn't need to be particularly efficient, because it will only be 
called for testing. A rather silly example:

def plus_one_testing(x):
    """Return x plus 1 using a different algorithm for testing."""
    if type(x) in (int, long):
        temp = 1
        for i in range(x-1):
            temp += 1
        return temp
    else:
        floating_part = x - int(x)
        return floating_part + plus_one_testing(int(x))

(The only problem is, if a test fails, you may not be sure whether it's 
because your test function is wrong or your production function.)

(3) Often you can check a few results by hand. Even if it takes you 
fifteen minutes, at least that gives you one good test. If need be, get a 
colleague to check your results.

(4) Test failure modes. It might be really hard to calculate func(arg) 
independently for all possible arguments, but if you know that func(obj) 
should fail, at least you can test that. E.g. it's hard to test whether 
or not you've downloaded the contents of a URL correctly without actually 
downloading it, but you know that http://example.com/ should fail because 
that domain doesn't exist.

(5) Test the consequences of your function rather than the exact results. 
E.g. if it's too difficult to calculate plus_one(x) independently:

assert plus_one(x) > x  # except for x = inf or -inf
assert plus_one( -plus_one(x) ) == x  # -(x+1)+1 = x

(6) While complete test coverage is the ideal you aspire to, any tests 
are better than no tests. But they have to be good tests to be useful. 
Even *one* test is better than no tests.



Hope this helps.



-- 
Steven



More information about the Python-list mailing list