开发者

static methods and unit tests

I've been reading that static methods tend to be avoided when using TDD because they tend to be h开发者_Python百科ard to mock. I find though, that the easiest thing to unit test is a static method that has simple functionality. Don't have to instantiate any classes, encourages methods that a simple, do one thing, are "standalone" etc.

Can someone explain this discrepancy between TDD best practices and pragmatic ease?

thanks, A


A static method is easy to test, but something that directly calls a static method generally is not easy to test independent of the static method it depends on. With a non-static method you can use a stub/mock/fake instance to ease testing, but if the code you're testing calls static methods it's effectively "hard-wired" to that static method.


The answer to the asked question is, in my opinion "Object Oriented seems to be all that TDD people think about."

Why? I don't know. Maybe they are all Java programmers who've been infected with the disease of making everything rely on six indirection layers, dependency injection and interface adapters.

Java programmers seem to love to make everything difficult up front in order to "save time later."

I advise applying some Agile principles to your TDD: If it isn't causing a problem then don't fix it. Don't over design.

In practice I find that if the static methods are tested well first then they are not going to be the cause of bugs in their callers.

If the static methods execute quickly then they don't need a mock.

If the static methods work with stuff from outside the program, then you might need a mock method. In this case you'd need to be able to simulate many different kinds of function behavior.

If you do need to mock a static method remember that there are ways to do it outside of OO programming.

For example, you can write scripts to process your source code into a test form that calls your mock function. You could link different object files that have different versions of the function into the test programs. You could use linker tricks to override the function definition (if it didn't get inlined). I am sure there are some more tricks I haven't listed here.


It's easy to test the static method. The problem is that there is no way to isolate your other code from that static method when testing the other code. The calling code is tightly-coupled to the static code.

A reference to a static method cannot be mocked by many mocking frameworks nor can it be overridden.

If you have a class that is making lots of static calls, then to test it you have to configure the global state of the application for all of those static calls - so maintenance becomes a nightmare. And if your test fails, then you don't know which bit of code caused the failure.

Getting this wrong, is one of the reasons that many developers think TDD is nonsense. They put in a huge maintenance effort for test results that only vaguely indicate what went wrong. If they'd only reduced the coupling between their units of code, then maintenance would be trivial and the test results specific.


That advice is true for the most part.. but not always. My comments are not C++ specific..

  1. writing tests for static methods (which are pure/stateless functions): i.e. the work off the inputs to produce a consistent result. e.g. Add below - will always give the same value given a particular set of inputs. There is no problem in writing tests for these or code that calls such pure static methods.
  2. writing tests for static methods which consume static state : e.g. GetAddCount() below. Calling it in multiple tests can yield different values. Hence one test can potentially harm the execution of another test - tests need to be independent. So now we need to introduce a method to reset static state such that each test can start from a clean slate (e.g. something like ResetCount()).
  3. Writing tests for code that accesses static methods but no source-code access to the dependency: Once again depends on the properties of the static methods themselves. However if they are gnarly, you have a difficult dependency. If the dependency is an object, then you could add a setter to the dependent type and set/inject a fake object for your tests. When the dependency is static, you may need a sizable refactoring before you can get tests running reliably. (e.g. Add an object middle-man dependency that delegates to the static method. Now plugin a fake middle-man for your tests)

Lets take an example

public class MyStaticClass
{
  static int __count = 0;
  public static int GetAddCount()
  {  return ++__count;  }

  public static int Add(int operand1, int operand2)
  {  return operand1 + operand2; }

  // needed for testability
  internal static void ResetCount()
  {
     __count = 0;
  }
}

...

//test1
MyStaticClass.Add(2,3);        // => 5
MyStaticClass.GetAddCount();    // => 1

// test2
MyStaticClass.Add(2,3);  // => 5
//MyStaticClass.ResetCount(); // needed for tests
MyStaticClass.GetAddCount();  // => unless Reset is done, it can differ from 1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜