First a recap to get us on the same page. With stubs and fakes I use this definition; stubs return canned answers while fakes are real implementations (with short cuts).
So what is the downside with testing with fakes? It all boils down to maintenance. A fake that works can actually make some testing simple since you don't need extra variables to keep track of data you want to verify but the problem arises when the fake has a bug. And I have yet to see a project where no test failed because of a buggy fake...
Think about it. A fake is a real implementation of the thing you want to fake. If you are faking an API for the file system, you need to implement a file system. If you are faking a database client, you need to implement the logic of the database and so on. So while a functioning fake can save developers time, the downside is when it does not behave like the real thing.
Here the stubs shine; a stub does exactly what you want. If you stub something so that it behaves different than the real thing it is super easy to fix; just change your canned response! No tedious debugging of the internals of a fake implementation somebody wrote half a year ago!
But the same way there are situations where mocks are preferable over stubs (which I covered last week); fakes are sometimes preferable over stubs! For simple behaviors the fake might be a good idea because for the simple things you are less likely to get it wrong. But that does not mean you always should use the fake for a certain dependency. Use the test double that makes most sense! Test ROI applies here too! If a partially functional fake can be created fast it might save time on some test cases. Especially if it is a dependency that is needed a lot.
And a final word of advice; if you do decide to implement a fake; make sure to add unit tests defining the behavior of your fake. That will make your life easier in the long run!