I'm using Python, if that's relevant.
As I understand it, a unit test is a test of the smallest portion of code that is relevant to a single use case. Often times this is a single function or method.
So when I'm testing my function, I do not want to test any functions that it's calling. When writing tests, that's reasonably easy to do: Just concern yourself with the logic that your function is doing.
But when I have the following situation:
def is_prime(number):
if number <= 1:
return False
for element in range(2, number):
if number % element == 0:
return False
return True
def extract_primes(lst):
return [item for item in list if is_prime(item)]
I'm confused about how a test for extract_primes
would look like.
def test_extract_primes():
lst = [0, 1, 2]
result = extract_primes_from_list(lst)
assert result == [2]
# and some more similar tests for empty lists, lists with only primes
# and lists with no primes
But now I'm just implicitly testing whether is_prime
is doing its work correctly. And when is_prime
has a bug, it will also cause the test for extract_primes
to fail, which beats the point of unit tests, where you want to quickly spot the single point of failure.
So it seems to me, that I shouldn't be calling is_prime
in the first place. So I end up with something like this:
@unittest.patch("path.to.module.is_prime")
def test_extract_primes(mock_is_prime):
mock_is_prime.return_value = True
lst = list(range(10))
result = extract_primes(lst)
assert result == lst
for i in lst:
assert mock_is_prime.called_with(i) # not sure if correct method name
But this is tiresome and inflexible at best. I cannot cause mock_is_prime
to return different values inside of the same test, unless I create a construct like:
bool_list = [True, False, True]
mock_is_prime.side_effect = lambda x: next(bool_list)
But that gets even more verbose. And as the number of function calls within the function under test increases, so does the stupid amount of boilerplate code to patch away those functions.
When I try to look for answers on the internet, I mostly get Java/C# dependency injection instructions, where they tell you to pass the correct needed object as parameter, and just create a dummy of that object for the method you're testing. Or I get debates about whether or not to test private methods. And that's fine, I understand those things. But I can't for the life of me figure out what to do with functions that depend on other function calls. Should I simply inject those functions?
def extract_primes(lst, is_prime_function=is_prime):
return [item for item in list if is_prime_function(item)]
And pass a dummy is_prime
function into extract_primes
under test? That just looks stupid and litters the function signature with weird parameters. And it's practically no different from patching the function away in the amount of work required. It just removes a single @patch
statement from the test.
So should I not be patching away functions in the first place? And if not, at what point does a function become worth patching away anyway? Only when it's manipulating the system? Or also when it's from a completely separate module?
I'm a little lost.
Aucun commentaire:
Enregistrer un commentaire