Agile Zone is brought to you in partnership with:

As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

Static Methods are Death to Testability

12.15.2008
| 33665 views |
  • submit to reddit

Recently many of you, after reading Guide to Testability, wrote to telling me there is nothing wrong with static methods. After all what can be easier to test than Math.abs()! And Math.abs() is static method! If abs() was on instance method, one would have to instantiate the object first, and that may prove to be a problem. (See how to think about the new operator, and class does real work)

The basic issue with static methods is they are procedural code. I have no idea how to unit-test procedural code. Unit-testing assumes that I can instantiate a piece of my application in isolation. During the instantiation I wire the dependencies with mocks/friendlies which replace the real dependencies. With procedural programing there is nothing to “wire” since there are no objects, the code and data are separate.

Here is another way of thinking about it. Unit-testing needs seems, seems is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. Seems work through polymorphism, we override/implement class/interface  and than wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override. Yes, static methods are easy to call, but if the static method calls another static method there is no way to overrider the called method dependency.

Lets do a mental exercise. Suppose your application has nothing but static methods. (Yes, code like that is possible to write, it is called procedural programming.) Now imagine the call graph of that application. If you try to execute a leaf method, you will have no issue setting up its state, and asserting all of the corner cases. The reason is that a leaf method makes no further calls. As you move further away from the leaves and closer to the root main() method it will be harder and harder to set up the state in your test and harder to assert things. Many things will become impossible to assert. Your tests will get progressively larger. Once you reach the main() method you  no longer have a unit-test (as your unit is the whole application) you now have a scenario test. Imagine that the application you are trying to test is a word processor. There is not much you can assert from the main method. 

We have already covered that global state is bad and how it makes your application hard to understand. If your application has no global state than all of the input for your static method must come from its arguments. Chances are very good that you can move the method as an instance method to one of the method’s arguments. (As in method(a,b) becomes a.method(b).) Once you move it you realized that that is where the method should have been to begin with. The use of static methods becomes even worse problem when the static methods start accessing the global state of the application. What about methods which take no arguments? Well, either methodX() returns a constant in which case there is nothing to test; it accesses global state, which is bad; or it is a factory.

Sometimes a static methods is a factory for other objects. This further exuberates the testing problem. In tests we rely on the fact that we can wire objects differently replacing important dependencies with mocks. Once a new operator is called we can not override the method with a sub-class. A caller of such a static factory is permanently bound to the concrete classes which the static factory method produced. In other words the damage of the static method is far beyond the static method itself. Butting object graph wiring and construction code into static method is extra bad, since object graph wiring is how we isolate things for testing.

“So leaf methods are ok to be static but other methods should not be?” I like to go a step further and simply say, static methods are not OK. The issue is that a methods starts off being a leaf and over time more and more code is added to them and they lose their positions as a leafs. It is way to easy to turn a leaf method into none-leaf method, the other way around is not so easy. Therefore a static leaf method is a slippery slope which is waiting to grow and become a problem. Static methods are procedural! In OO language stick to OO. And as far as Math.abs(-5) goes, I think Java got it wrong. I really want to write -5.abs(). Ruby got that one right.

From Misko Hevery - The Testability Explorer Blog

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ronald Miura replied on Mon, 2008/12/15 - 1:49pm

Are functional languages completely untestable?

Should Strings have all methods in StringUtils classes out there, or should we be able to 'add' random methods to it?

Should Integers have methods completely out of context, like .days(), .minutes(), .hours(), .dolars(), .yens(), .pounds(), .kilograms(), etc. (plus math operations, of course), like many Ruby look-how-easily-I-create-a-english-like-dsl-ma examples out there?

I don't know if Fowler would call this an HumaneInterface, but I'd call it KitchenSinkInterface.

Functions shouldn't have context inveolved. Shouldn't have side effects. Shouldn't call Hibernate from a Session taken from an obscure ThreadLocal variable. If it does any of this, the problem is not the static method, but your crappy design.

Thomas Mueller replied on Mon, 2008/12/15 - 1:49pm

> there is nothing wrong with static methods

There still is nothing wrong.

> I have no idea how to unit-test procedural code.

What? You don't know how to test Math.abs? By the way, Math.abs doesn't always return a positive value.

 

 

Pieter Zaad replied on Mon, 2008/12/15 - 2:34pm

> Functions shouldn't have context inveolved

This is not about functional programming, as far as I can see, so no such rule applies.

> Should Integers have methods completely out of context,

True or not, static methods are still an annoyance for testing.

> What? You don't know how to test Math.abs?

This shows a complete misunderstanding of the article.

Artur Biesiadowski replied on Mon, 2008/12/15 - 2:45pm

I would generally agree, except the cases with math methods. You cannot start adding all the operations to basic classes. Double will most probably not have multiply(Vector3f) method in it, so you have to put it in Vector3f and remember to always call in correct order. But if you then have another class from 3rd party library, there is no good place to put operation between them.

Additionally, you mention issues with unit tests, injecting mock environment etc. How having abs method defined on Double would improve it's testability? What kind of mock environment would you like to inject into Double to make abs working in different way?

There are rules and there is common sense. Don't let the rules to take over.

Ola Bildtsen replied on Mon, 2008/12/15 - 3:18pm

"I like to go a step further and simply say, static methods are not OK"

I simply can't accept such sweeping statements about using language features because of problems with testability.  Testability is secondary to good design, sound decisions and proper code development -- if the problem calls for a static method, it should be implemented as such. 

This and most other test-oriented articles I've read discount the value of proper skills and decision-making in developers, and instead enforces sweeping and often hampering rules in the name of "testability".  I would love to see more time and effort dedicated to fostering better developers instead of turning them into testing drones that aren't allowed to create and think independently.

Yo Tu replied on Mon, 2008/12/15 - 4:06pm

How does a DI framework test itself? Do they create a DI metaframework to test it? How they test the metaframework? ...ranting goes on forever...

Thomas Mueller replied on Mon, 2008/12/15 - 4:23pm

The main problems of the article are that there are no actual examples. What looks like an examples (Math.abs()) is a counter-example. There is nothing wrong with procedural programming. There is nothing wrong with static utility methods, and in most cases it's actually easy to test them.

Example: how does -5.abs() improve testability?

How do you test this, and where is the bug:

static final String[] CACHE = new String[10];
static synchronized String getCached(String s) {
int x = Math.abs(s.hashCode()) % 10;
if (!s.equals(CACHE[x])) CACHE[x] = s;
return CACHE[x];
}

phil swenson replied on Mon, 2008/12/15 - 5:14pm

Most static methods are simple stateless utility functions.  Sometimes functions are very appropriate and OO constructs are overkill.  Take for example, StringUtil.extractHost(String url) to parse out the host from a url.  Why over-engineer it?  It's simple to write and simple to test.

Alex(JAlexoid) ... replied on Mon, 2008/12/15 - 6:30pm

Yeah, yeah... The way we test now and how test frameworks work is not ideal. But we should not start blaming everything else for not being ideal for the tools we create.(It's like blaming a fork, while eating a soup with it)
It's more about the tools that don't support it.

Even global state is testable. In Java it's not that hard. Just do a little play with ClassLoaders. It's more load on the system, but possible. With a little hackery, we can have a tool to mock the static calls, for tests.

Who can forget the tonns of tests that people developing in C have created.

So to sum up: How blind of you to blame everything else for the deficincies of the tools. 

Will you be writing up on the difficulties of concurrency testing? Should we ban that one?

Jess Holle replied on Mon, 2008/12/15 - 7:43pm

Just use good testing tools, e.g. JMockit, and you'll be able to mock out pretty much whatever you need to, static or not.

Many of the language constructs that are supposedly death to testability are just death to under-powered testing tools.

Some argue that code becomes intrinsically better by contorting it to meet the demands of tools like EasyMock.  I disagree.  In general if there are real, non-testing requirements on the code that argue for it being refactored, great -- but refactoring stable, working code to meet the whims of testing tools has always struck me as a patently bad idea.  It's likely to be a waste of time, may destabilize the code, and opens degrees of freedom for testing purposes that may be quite inappropriate during normal operation yet cannot be reasonably prevented.  For instance, normal operating constraints may imply that a given field or method should be private or final or static for best clarity, encapsulation, safety, etc -- whereas under-powered testing tools may demand that you reverse these decisions simply to meet their whims.

Mario Schwede replied on Mon, 2008/12/15 - 8:02pm

[quote]Static methods are procedural! In OO language stick to OO. And as far as Math.abs(-5) goes, I think Java got it wrong. I really want to write -5.abs(). Ruby got that one right. [/quote]

It is because in java there are primitive datatypes. Another problem ist, that the class java.lang.Number is inheritable and it should break backward compatibility to add methods.

By the way java.math.BigDecimal has a abs() Method. Groovy always create BigDecimals to offer something like -3.abs().

Im sure, if Sun were not forced to provide backward compatibilty, such problems were already refactored. The main lessen is, that Data and Behavior should stick together.

A Static Method with one or more Parameter can easy be replaced by an object, that wraps the parameter and offer the needed funtionanlity. The dark side is, that you always have to create a new objekt.

Im not really sure how expensive this is. But I know that the garbage collector is optimized for short living object, so the memory usage should not be a problem.

Franz Allan See replied on Mon, 2008/12/15 - 9:50pm

I think you are referring to 'Seams' and not 'Seems'.

Anyway, it is true that statics makes testing harder. But not all the time. Statics make it harder to test when

  • The static is complex
  • Your System Under Test (SUT) depends on a static ( Not to be confused with testing the actual statics which is easy ).

However, it is still not totally impossible (and may not even be that hard). Jess mentioned about JMockit which could mock even static methods ( I personally never tried it, but that does sound interesting ).

Or even if you're using a Mocking tool which does not support static mocking, you could still wrap around your static invocation into a utility method / class so that you can swap it with somethng else during testing ( thereby creating your Seam ).

Or if you want the most straight forward approach, just test your SUT with those statics dependencies. If those statc dependencies are simple enough like Math.abs(...), then why bother mocking it?

Jeroen Wenting replied on Tue, 2008/12/16 - 1:21am

You're looking at a procedural paradigm from an object oriented point of view, not a procedural point of view.

 As a result you're looking at each procedure as a separate code block, as if it were a separate class, when clearly it isn't.
I don't hear you complain about being unable to test the public methods of a class in separation from the private methods they call. This is basically the same thing, some methods call other methods and therefore can only be tested if the methods they call are known to function correctly.

Maybe current OO testing frameworks are less than ideal for testing such procedural code because they never took it into account when being designed, but that's a problem more of the testing framework than it is of the code undergoing testing.
Procedural code has been written, and tested, for decades before any OO language ever arrived on the scene, so any failure to be able to test it must be solely with the person failing to test it rather than with the code (or rather with the general principle under which the code was written, individual implementations of course can always be poorly designed).

I do agree with the person who said that one has to wonder who tests the tests. How do I know my testsuite isn't buggy and yields false positives (or false negatives)? And how do I know the code that tests the testsuite is itself correct?
Those are far greater issues than the testability of code written using any particular paradigm or principle.

Tormod Overlier replied on Tue, 2008/12/16 - 2:27am

Am I the only one that totally agrees with the author?

Tom Meier replied on Tue, 2008/12/16 - 2:35am

Static methods are ok and especially easy to test, as long as they don't use static members. No static members = they're stateless / utility methods. Moderately used they're absolutely ok. Using static members is what kills testability.

Tom

Johan Haleby replied on Tue, 2008/12/16 - 2:35am

While I agree that you probably should avoid the use of static methods in most cases I think nowadays it's not mainly because of the unit-testability aspect which many seem to imply. There are several Java frameworks that let's you mock static methods quite easily, JMockit is one and PowerMock (which I'm a founder of) is another. With frameworks like these many of the things normally regarded as untestable needs to be reevaluated. PowerMock is for example able to mock static, private & final methods, suppress constructors and initializers, mock new construction, access internal state and many other things normally concidered difficult or impossible to mock/test. You shouldn't need to refactor your code for the testability aspect alone. If that's the only reason you have for refactoring you probably shouldn't. Frameworks like these also gives you more design options when doing TDD.

Jeroen Wenting replied on Tue, 2008/12/16 - 5:16am in response to: Tormod Overlier

[quote=toverlier]Am I the only one that totally agrees with the author?[/quote]

Looks like it, which may stem from you not understanding something fundamental:

We're talking about whether static methods can be tested, not whether they're an appropriate construct for general use.

Casper Bang replied on Tue, 2008/12/16 - 6:47am

The problem is not with static methods nor procedural code, but with state. And a static factory is nothing more than a pretty looking constructur where you can use polymorphism, in fact, I'd argue that a static factory method is a lot easier to test than a normal constructor.

Jeroen Wenting replied on Tue, 2008/12/16 - 8:00am

except insofar that static factories normally call constructors so testing one implicitly tests the other.

Casper Bang replied on Tue, 2008/12/16 - 9:14am

Sure, in that if no object were ever constructed you wouldn't have a very interesting program at all. The only problem with static factory methods are 1) when caching is done and 2) the inability of the Java compiler to infer effectively final fields.

Franz Allan See replied on Tue, 2008/12/16 - 10:03am in response to: Casper Bang

Based from the article, I'm assuming the author is more of a behaviorist than a statist when it comes to test. 

I think the author's basic premise is that an SUT with a static method depends-on is hard to unit test because you cannot directly apply a seam to decouple the SUT from its depend on.

If so then:

a.) Others have mentioned that there are mocking frameworks now that can mock even static methods,

b.) Even if you don't mock static methods, you can wrap them around something else that you can mock or apply a seam to ( as opposed to the tone of the article which suggests that you cannot apply seams to SUTs with static methods depends-on just because static methods are stateless), and

c.) If the static method is simple enough ( i.e. Math.abs(...) ), why bother decoupling your SUT from it? Might as well proceed with your test as is since the static method does not add significant complexity to the test.

 

My 2 cents worth.

Ivan Lazarte replied on Wed, 2008/12/17 - 12:06am

Who will test the testers?

 I think I've just blown myself away. 

Alessandro Puzielli replied on Wed, 2008/12/17 - 9:48am

I don't understand:

1) I code static method for general and common function: formatting a

java.sql.Date

for JSP page, compare two class, validatio, mathematical need.

 

2)for debug: i code static the number of object of a class

 

You says  that is diffucult for the testing? I say that the static methods must not be necessary for the business logic but only for the secondary aspects. Do you prefer - by default - testing ALL the functions or only the new functions whenyou must deploy a new feature of your software?

 

Grame smith1 replied on Tue, 2009/03/31 - 6:44am

The reason is that a leaf method makes no further calls. (logo design company) As you move further away from the leaves and closer to the root main() method it will be harder and harder to set up the state in your test and harder to assert things. (brochure design) Many things will become impossible to assert. Your tests will get progressively larger. (Animated Logo Design). In fact, I'd argue that a static factory method is a lot easier to test than a normal constructor.

sdadfam faomdm replied on Sun, 2009/04/19 - 4:50am

I say that the static methods must not be necessary for the business logic but only for the secondary aspects. Do you prefer - by default - testing ALL the functions or only the new functions whenyou must deploy a new feature of your software?

regards,

 Car games

sdadfam faomdm replied on Sun, 2009/04/19 - 4:58am in response to: Jeroen Wenting

You says that is diffucult for the testing car games ? I say that the static methods must not be necessary for the business logic but only for the secondary aspects. Do you prefer - by default - testing ALL the functions or only the new functions whenyou must deploy a new feature of your software?

prince aus replied on Fri, 2009/04/24 - 3:56am

static methods must not be necessary for the business Missionary Airfare Travel logic but only for the secondary aspects. Do you prefer - by default - testing ALL the functions or only the new functions whenyou must deploy a new feature of your software?

Rogerio Liesenfeld replied on Sat, 2009/06/27 - 10:38pm

I think we should consider the bigger picture: is it valid to exclude certain Java language keywords and idioms for the sake of unit testing?

Sure, static is non-OO, but does that make using it always bad?
And what about declaring classes and methods final, or using the new operator to obtain dependencies from inside the SUT itself? Is there anything non-OO about that, or anything else that makes it always bad design?

Is it acceptable to have lots of Java interfaces in production code which never get a second implementation? That is bad design, and unfortunately it seems to happen all too often in real projects, for whatever reasons...

With the right testing tool (and such tools exist not only for Java, but also for .NET and even Ruby), simple and elegant unit tests can always be written, so testability is not necessarily an issue.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.