The short answer is "No". The more interesting part is why/how this situation might arise.
I think the confusion is arising because you're trying to adhere to strict testing practices (unit tests vs integration tests, mocking, etc.) for code which doesn't seem to adhere to strict practices.
That's not to say the code is "wrong", or that particular practices are better than others. Simply that some of the assumptions made by the testing practices may not apply in this situation, and it may help to use a similar level of "strictness" in coding practices and testing practices; or at least, to acknowledge that they might be unbalanced, which will cause some aspects to be inapplicable or redundant.
The most obvious reason is that your function is performing two different tasks:
- Looking up a
Person
based on their name. This requires integration testing, to make sure it can find Person
objects which are presumably created/stored elsewhere.
- Calculating whether a
Person
is old enough, based on their gender. This requires unit testing, to make sure the calculation performs as expected.
By grouping these tasks together into one block of code, you can't run one without the other. When you want to unit test the calculations, you're forced to look up a Person
(either from a real database or from a stub/mock). When you want to test that the lookup integrates with the rest of the system, you're forced to also perform a calculation on the age. What should we do with that calculation? Should we ignore it, or check it? That seems to be the exact predicament you're describing in your question.
If we imagine an alternative, we might have the calculation on its own:
def is_old_enough?(person)
if person.male?
return person.age > 21
else
return person.age > 18
end
end
Since this is a pure calculation, we don't need to perform integration tests on it.
We might also be tempted to write the lookup task separately too:
def person_from_name(name = 'filip')
return Person::API.new(name)
end
However, in this case the functionality is so close to Person::API.new
that I'd say you should be using that instead (if the default name is necessary, would it be better stored elsewhere, like a class attribute?).
When writing integration tests for Person::API.new
(or person_from_name
) all you need to care about is whether you get back the expected Person
; all of the age-based calculations are taken care of elsewhere, so your integration tests can ignore them.