Last 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