“Why Too Much Mocking is Bad” and Other Questions

questionMark-backgroundLast webinar, “10 secret unit testing tips” was a blast. There were many questions, some I’ve answered. But then I’ve looked at the clock, and discovered I neglected to answer a few. And since I’m a a man of my word (most of the time), here are the rest of the questions and comments and my answers. You might want to check out the webinar first for some context.

Let’s start with a triplet:

Q: How to write tests for functions with global variables?
Q:How to write tests for functions that use controls (TextBox, etc.)?
Q: How to write tests for static functions?

A: Like many of my answers, it depends. Are all these things public? Then doing the “Act” part of the test (e.g. invoking the method) is probably not a big problem. But what if it has arguments that cannot be created? Or maybe the object-under-test crashes upon creation? 

Now what about the static or global data? Some of it may not be accessible. Or TextBox controls that are created in the object itself?
Setting up objects like like that can be solved with isolation frameworks (the powerful ones at least). That includes private methods as well. Check out the Typemock site for these solutions. However, not all technologies are created equal (and for now Isolator only helps in C#, VB and C++). Find the best framework you can, and if you can’t fake it, you might need to change the code in order to test it. It’s risky, so go in with your eyes open.

C: TDD goes strongly in the direction of decoupling.
A: TDD is a methodology so it doesn’t go either for or against decoupling. Using bare-bones TDD (like hand-written mocks) or writing TDD style with a framework that requires working with interfaces and therefore encourage decoupling. But what if your code uses SharePoint? No interfaces. You’ll need to add more abstractions, which might get your framework happy, but you’ll wind up with a layered design, with no real use other than, well, making the framework happy. My advice: Design the way you want to, then choose the tools that support it, not the other way around.

Q: If a function has a wide variety of inputs, how much do you test it?
A: Short answer: until you’re happy and confident the code is tested enough.  Shorter answer: Until someone stops you.

Q: Why is too much mocking bad?
A: Like everything, too much of a good thing… Seriously, there are basically 2 reasons.

  • Using mocking/stubbing/faking is coupling the test to the innards of the production code. The more use use them, the more it makes the test brittle, and susceptible to code changes. We don’t like to repeatedly rewrite the test, just because we changed the code for making it work.
  • Isolating the production code from all its dependencies will probably guarantee that your code-under-test works. However, later on, other parts of the code may change. Your unit test still passes, but in integration the implementation fails. This test can be a false positive, and obviously not that effective.

Don’t get me wrong – you need isolation, and should use it when possible to make tests run faster, or control the uncontrollable (simulate an exception on the wire, without using scissors, for examples). But for other cases, think before you fake.

C: For successful design through TDD, the person needs to be well versed in design principles, identifying code smells and refactoring.
A: True, and not just for TDD – for writing code in general.

Q: How can we test code that runs within a thread? How do we test cases that are affected when a thread runs?
A: That’s a whole webinar by itself. Let’s start with some guidelines:

  • Try to test the logic that runs in a thread, only outside of the thread. You may not need to run a thread: you may be able to just call the method on your current thread.
  • If that doesn’t work, you’re in for some work. You may be able to fake some threading APIs (for example, instead of starting a working thread that goes to the database, and our code waits for it, you can fake a thread that sleeps for 5 seconds then joins. Isolator can do that).
  • If you’ve got threads mixed together, synchronized and sharing data, and you can’t do the first too, it’s OK to use an integration test that tests the whole code. If it’s important code, it’s important to test – unit and integration if possible.

For other questions like “Should we test private method” (the surprising answer is “it depends”) and others refer to the Q&A section of the webinar, or who knows, maybe it will appear in length in a webinar soon.
Gil Zilberfeld

  • http://www.yegor256.com/ Yegor Bugayenko

    Maybe this article will also be interesting, on this very subject: http://www.yegor256.com/2014/09/23/built-in-fake-objects.html

    • typemock

      Thanks for the article, the idea is nice, although every change in the API will need to be implemented in mocks too. and in memory databases dont have all the bits and bytes of the real database. Why not use the recursive fake mechanism:

      var fakeRegion = Isolator.Fake.Instance(); // all hierarchy is faked
      fakeRegion.table(“employees”).frame.attibute(“name”) = “Jeff”; // setters are automatic
      fakeRegion.table(“employees”).frame.attibute(“salary”) = “50000″;

      • http://www.yegor256.com/ Yegor Bugayenko

        Yes, you’re right, every change in the library has to be implemented in the fake/mock counterpart. And, of course, fake classes can’t emulate all the features of live classes. But still, using them is a much better alternative than mocking frameworks. Recursive fake, as you suggested, may work in Ruby or JS, but Java is more strict :)

        • typemock

          That code works for C# /.NET , I guess we need to support Java too :-)

TOP