The real problem here is you have simplified all the meaning out of your code. All that is left is a structural representation. You want us to answer based on the structure so that you have a structure based rule to follow blindly without having to think about meaning, context, or semantics.
Not that I wouldn't want to know such a rule if it existed. I just know that it doesn't and stopped looking a long time ago.
But if you're asking this question I think I know where your head is so let me point you in some good directions:
If I am testing func a()
If all you want is to test then you can test
a() by running any code that uses it. Honestly, any code. You can do this by hand. If you actually mean you want to create some automated tests then...
should I mock all the other function calls within that method or only mock the dependencies I have outside of this class altogether? For example, mocking database calls, etc.
Consider how tragic it is to mix together a test that takes 0.01 seconds to run with a test that takes 3 minutes because now they collectively take 180.01 seconds to run.
I bring this up because when you're reading articles about testing and they brag about how they stopped a test from hitting the database, the lesson isn't "never hit the DB with a test". It's "my DB was slow so I figured out how to do without it when testing other stuff, you can do it too".
So when you ask me what to do with
a(b(c(d(e())))) my answer is test EVERYTHING but mock out the slow stuff and put those fast tests in a different pile.
d() is slow, for whatever reason, (DB, filesystem, prime number calculator, or web cam of a lava lamp that generates random) then mock
By slow I mean
e() are blazingly fast by comparison. So much so that they would live in your "run me every time" test pile.
The point of mocking
d() isn't that it uses an evil test hating DB. It's that it is preventing you from testing other stuff quickly.
These fast tests are so useful that many IDE's will now let you run them without clicking anything. They run while you type like you IDE's compiler does when giving hints and underlining stuff because of a compiler error. It's like fast tests become part of your compiler.
Compiler errors are also fast so why shouldn't they be in the fast pile?
Just keep the slow tests out. I don't know about you but I hate waiting while I'm typing.
I'm in favor of small isolated tests. But not because I believe every class and method needs its own test. I actually hate that idea. It discourages decomposition. No, what I want is small tests that limit what I feel like I have to read to understand
a(). That is it. My test boundaries are all about reading code. Not about it's structure. Heck I only care about test speed because it slows down reading. It's all about reading. Why? Because if I can read it easily I know if it works. I add tests to make reading code go faster.
Should you mock
b() when testing
a()? If they're both fast then how hard is it to understand
b() living inside it? I wouldn't mock
b() unless readability demanded it. That means you could just test:
a(b(c(dMock(e())))) and be done with it, if you're sure newbies can follow that. If they can't keep breaking it down smaller until they can. Always consider the newbies.
There is one kinda structural consideration worthy of note here but it's still really a readability issue. Public things have many clients while private have few. I'm much more likely to isolate
b() for testing and make its intended use clear if it's public. That sounds structural but the reason is: if I have to check every bit of code that uses it, that's a lot of code to read. But private functions take a lot of that pressure off.
You might have noticed by now that I've really ignored the fact that they are functions. I really don't care if they are functions, objects, closures, or subroutines. I can rewrite
a() to do all this stuff without functions. What matters is segregating test speed and readability. I'd break
a() itself up into more functions if it would get me that.
Kinds of tests
You might have also noticed I didn't call anything a unit test or integration test. That's because I've now read enough conflicting explanations of these names that I'm sick of trying to explain what I really mean when I use them. There's a slow test pile and a fast test pile. You can name them Alice and Bob for all I care.
Well no, I take it back. Whatever you do, please give them better names then that.