Over time I have found fakes to be significantly better than mocks for most testing. They allow refactoring internal code without failing all the tests, and you can always simulate an error path by overriding a specific method in the fake.
What’s the difference between a mock and a fake?
A fake is a full implementation that takes shortcuts in its implementation. For example, a fake database could store the data in-memory and not guarantee transactions to be isolated. But it would accept inserts, updates and deletes like a real database.
A mock is a barebones implementation that confirms the methods were called in specific order and with specific values, but without understanding these values.
For example, I used to have a MockLogger, it would implement the .info, .warning and .error as jest.fn() functions, and then I have to check that the functions were called with the appropriate values. But now I have a FakeLogger that implements the methods by saving the logged messages to an array, and then I check that the array contains the messages I want.
https://martinfowler.com/bliki/TestDouble.html though it is fairly common to talk of Mocks when a different kind of Test Double is meant
My own answer to the question is: It makes when you are testing the logic of “coordination” code, and that logic is in itself sufficiently complex.
Imagine a user signup flow where items are inserted into a database, emails are sent, perhaps slack alerts, where are there possible failure points halfway through that need to be logged, customer service notified, etc. If we want to test that logic, and have control over the different code paths to exercise the various possibilities, mocking with dependency injection is the way to do it. Especially because that logic per se is not concerned with the actual database, the actual email system, and so on… but only with how those pieces coordinate together.