Search This Blog

Loading...

Monday, October 13, 2008

Static Method, Is It that Harmful?

Static method is much more easier to learn, and use compare to OOP and polymorphism,. But somehow it acquires a bad name in the TDD circles. Some people don't like static methods . The reason? Can't mock the static methods.

I agree that mocking is good , and sure, I do use a lot of mocking in my code . but the very inability to mock something shouldn't be the reason to reject it. For starters, static method does have a lot of virtues. It is clean, for the inputs and outputs are plain visible just from the method signatures. You don't have to guess what are the other inputs that are required by that method. All the inputs are available as the method parameters, and all the outputs are available as the return values ( or the ref or out parameters). The same cannot be said of OOP and polymorphism. You can inherit a class, invoke a method inside the class and get a NullReferenceException  simply because you forget to set one of the crucial variable that is required for the method to work successfully. In this sense static method is actually easier to understand and easier to work with.

Not only that, sometimes using static method is more natural than using inheritance and all that is related to the fanciful OOP. I can't think of an example on the fly now but I am sure you will agree with me on this point because you have encountered this kind of situation before.

Now, coming back to the issue of mocking. Some mocking frameworks can't mock static method, because in the process of mocking they are actually subclassing the mocked class and fill in all the fake values and pretend that this is mocking. Personally I have nothing against this technique. As long as it works, who cares. But the trouble is no one can override a static method. And so, no mocking for static method. Since we know mocking is good, hence static method is bad.

Instead of dismissing static method out of hand, why not just take the other approach? Why not just improve the mocking framework so that it can handle static method? Instead of dynamically subclassing mocked class, why not just use AOP to intercept the method calls and do all the mocking you want (This is how TypeMock  does the mocking)? In this way you can mock classes and objects, and you can mock static methods, too. Isn't that simply great?

Banning static method--even though it is perfectly sound and can improve readability-- simply because the mocking frameworks can't handle it seems to me like fixing the symptom rather than the root cause. And yes, the "fix" is going to bring another set of trouble and chaos.

Is it worth it? I doubt.

13 comments:

redsolo said...

Static methods hide dependencies. If a class (A) uses a lot of static methods in other classes (B,C), it is not evident that class (A) is dependent on class (B,C) until you start looking into the code itself.

This has nothing to do about mocking, it is about maintenance (or the guy coming after me).

Soon Hui said...

Static methods hide dependencies. If a class (A) uses a lot of static methods in other classes (B,C), it is not evident that class (A) is dependent on class (B,C) until you start looking into the code itself.

Redsolo, the dependencies hiding is also apply to Inheritance, I think.

asbrown said...

Inheritance is overused and should really only be used when subclasses are literally sub-types of the parent class and use all of the parent's attributes and functionality. Inheritance should definitely not be used simply to share functionality.

Composition is a much better approach - for example the strategy pattern for injecting algorithms into objects that share an interface, but are not strictly speaking members of the same class hierarchy. Composition is also useful for explicitly declaring dependencies via the API - either constructor arguments or setters. This also allows you to easily mock out dependencies, getting around the sticky issue of testing a class with static references.

Further, static methods don't/can't require object state, so why put them in a class hierarchy? Rather, consider moving such methods to services that can operate on an interface. At that point, if you were just using inheritance to share functionality, you don't tie yourself into a bastardized object model. You just end up with a service that operates on similar objects via polymorphism.

This latter approach gives you the best of both worlds - you have explicit service level API's that rigidly declare inputs and outputs and, because service dependencies are often satisfied via composition, you have a highly decoupled and easily testable service layer.

I agree you could (presumably) change the mocking frameworks to support mocking statics. But that's kind of skirting the issue. Architectures that are high decoupled are simply easier to test and easier to refactor because you don't have hidden and hard to change dependencies.

asbrown said...

this is a thread that speaks precisely to this issue - some good stuff to chew on.

http://www.infoq.com/news/2007/12/does-di-pay-off

Anonymous said...

Associating mock objects with TDD is a bit silly.

Anybody sitting around thinking about tests is wasting their time and probably the time of the person paying them.

As for statics, well build your system without them. And good luck.

It would be alot more informative an article if you dropped the loaded language and just talked about how to test with statics present.

Mock objects turn code into mush, and make the entire class heirarchy dependent on them.

So much for new. Lets stop being pretentious and making the factory pattern into the jesus christ of OOP.

It aint, and when over-used/abused its a stupid waste of time.

Soon Hui said...

It would be alot more informative an article if you dropped the loaded language and just talked about how to test with statics present.

As I said, TypeMock can mock static method, so it shouldn't be too difficult after all.

Ricky Clarkson said...

What would a mock of this method (static or otherwise) do differently to the method itself? (using [] for generics)

public [T] identity(T t) { return t; }

Soon Hui said...

Ricky,

A mock of this method will, when this method is called, replaced this method call with a fake return value. So this method will not be actually called, instead, a fake value will be returned.

Ricky Clarkson said...

Ok, great. Can you show such a mock for that method?

Anonymous said...

If you test all of your code, you also test the static method. If they behave as expected, why would you need to mock them? The only drawback is that you actually have to implement the static method first, but in my experience they usually contain very basic functionality, such as a singleton getInstance() method.

For anything more complex the chance for change or customization is just too high, so I would suggest using instance methods so the behaviour can be altered either by inheritance or composition. But that doesn't mean that the odd static method is not appropiate.

Blackwasp said...

I completely agree. Many times I have heard developers not using the functionality and flexibility of a language because their preferred tool (testing or otherwise) does not work well with it.

The answer should always be. 1) Don't compromise your code. 2) Find a better tool.

Soon Hui said...

ricky, sorry for the delayed response:

Ok, great. Can you show such a mock for that method?

Here's the method under test:


public class AnElement
{
public int Variables
{
get;
set;
}

public static T Identity[T] (T myself)
{
return myself;
}
}


And here's how you can write the test with TypeMock AAA syntax

[Test, Isolated]
public void TestAnElementMySlef()
{
Isolate.Fake.StaticMethods[AnElement](Members.MustSpecifyReturnValues);
Isolate.WhenCalled(() => AnElement.Identity[int](5)).WillReturn(10);
Assert.AreEqual(10, AnElement.Identity[int](5));
}

Eli Lopian said...

Soon,
Great article...

You don't need to call:

Isolate.Fake.StaticMethods[AnElement]();

using Isolate.WhenCalled is enough.