7

Problem Statement:

I have a class that does some validation and it looks something like this:

func a() {b()}
func b() {c()}
func c() {d()}
func d() {e()}
func e() {return}

This is a simplified view, but the idea is that these function calls are dependents of one another as I am doing some transformations and validations in each.

Question:

If I am testing func a() 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.

  • 3
    I find that when I am hitting situations like this, it is usually a sign that my class is doing too much. There is usually another "class" hidden in there that should be extracted. – Caleb May 31 '18 at 15:35
  • 6
    And in the rare cases where that is not the case. I would still avoid "self mocking". Tests should check behavior, not just implementation details. – Caleb May 31 '18 at 15:37
  • 1
    Question: are b(), c(), ... etc. public methods for any reason? I agree with @Caleb, you want to test public behavior, not implementation details. The reason I ask the question is that you might need to build up your tests from the most specific public method to the least specific. That allows you to focus on the behavior required at each level--and skip tests for private methods/functions. – Berin Loritsch May 31 '18 at 16:16
  • 1
  • 4
    Aside: this is a very strong case for writing your tests before your implementation. You'll find out about smells like this before they happen, because the test will be hard to write. Or, more accurately, you won't write a complicated test for code that doesn't exist and the code that follows will be naturally simple too. – Ant P May 31 '18 at 16:50
10

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.

Speed

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.

If d() is slow, for whatever reason, (DB, filesystem, prime number calculator, or web cam of a lava lamp that generates random) then mock d().

By slow I mean a() b() c() and 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.

Readability

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 a() with 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.

Functions

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.

  • Nice answer, totally agreed. Only fast and slow tests you should consider about. Forget unit, integration, acceptence and other categories – Fabio Jun 2 '18 at 11:58
2

If I am testing func a() should I mock all the other function calls within that method

If you want to test the function a then you just need to mock b. This means b's dependency chain is unimportant to the test of a.

If you didn't then you would be testing a AND b, c, d etc. Which is not the same as testing a

or only mock the dependencies I have outside of this class altogether? For example, mocking database calls, etc.

If you want to test the class containing a, then you need to mock dependencies from outside the class.

If you don't then you are testing the class, plus its dependencies.

Ideally you want to have tests for the smallest 'unit' possible, I would say that this is usually the class rather than its individual methods. Leaving functional programming aside and assuming a OOP or ADM + services style of programming.

In theory if all your 'units' are tested and passing then your program works.

In practice, you will also want to test the whole thing working together as well.

If you just do these 'integration' tests and they all pass then your program also works.

One of the advantage of unit tests is they tell you where the problem is.

The advantage of integration tests is they tell you if your program actually works.

You should do both.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.