First, some definitions:
A unit test tests units in isolation from other units, but what that means is not concretely defined by any authoritative source, so let's define it a bit better: If I/O boundaries are crossed (whether that I/O is network, disk, screen, or UI input), there's a semi-objective place we can draw a line. If the code depends on I/O, it's crossing a unit boundary, and therefore it will need to mock the unit responsible for that I/O.
Under that definition, I don't see a compelling reason to mock things like pure functions, meaning that unit testing lends itself to pure functions, or functions without side-effects.
If you want to unit test units with effects, the units responsible for the effects should be mocked, but perhaps you should consider an integration test, instead. So, the short answer is: "if you need to mock, ask yourself if what you really need is an integration test." But there's a better, longer answer here, and the rabbit hole goes much deeper. Mocks may be my favorite code smell because there's so much to learn from them.
Code Smells
For this, we'll turn to Wikipedia:
In computer programming, a code smell is any characteristic in the source code of a program that possibly indicates a deeper problem.
It continues later...
"Smells are certain structures in the code that indicate violation of
fundamental design principles and negatively impact design
quality". Suryanarayana, Girish (November 2014). Refactoring for Software Design Smells. Morgan Kaufmann. p. 258.
Code smells are usually not bugs; they are not technically incorrect and do not prevent the program from functioning.
Instead, they indicate weaknesses in design that may slow down
development or increase the risk of bugs or failures in the future.
In other words, not all code smells are bad. Instead, they are common indications that something might not be expressed in its optimal form, and the smell may indicate an opportunity to improve the code in question.
In the case of mocking, the smell indicates that the units which seem to be calling for mocks depend on the units to be mocked. It may be an indication that we haven't decomposed the problem into atomically-solvable pieces, and that could indicate a design flaw in the software.
The essence of all software development is the process of breaking a large problem down into smaller, independent pieces (decomposition) and composing the solutions together to form an application that solves the large problem (composition).
Mocking is required when the units used to break the large problem down into smaller parts depend on each other. Put another way, mocking is required when our supposed atomic units of composition are not really atomic, and our decomposition strategy has failed to decompose the larger problem into smaller, independent problems to be solved.
What makes mocking a code smell is not that there's anything inherently wrong with mocking - sometimes it is very useful. What makes it a code smell is that it could indicate a problematic source of coupling in your application. Sometimes removing that source of coupling is much more productive than writing a mock.
There are many kinds of coupling, and some are better than others. Understanding that mocks are a code smell can teach you to identify and avoid the worst kinds early in the application design lifecycle, before the smell develops into something worse.