22

I'm asking because I'm trying to use a mocking framework (Mockito) which does not allow you to mock static methods. Looking into it I've found quite a few blog posts saying that you should have as few static methods as possible, but I'm having difficulty wrapping my head around why. Specifically why methods that don't modify the global state and are basically helper methods. For instance I have a class called ApiCaller that has several static methods. One of the static method's purpose is to execute an HTTP call, deal with any custom issues our server might have returned (ex. user not logged in) and return the response. To simplify, something like:

public class ApiCaller {
...
   public static String makeHttpCall(Url url) {
        // Performs logic to retrieve response and deal with custom server errors
        ...
        return response;
   }
}

To use this all I have to do is call ApiCaller.makeHttpCall(url) Now I could easily make this a non static method like:

public class ApiCaller {
...
   public String makeHttpCall(Url url) {
        // Performs logic to retrieve response and deal with custom server errors
        ...
        return response;
   }
}

and then to use this method call new ApiCaller().makeHttpCall() but this just seems like extra overhead. Can anyone explain why this is bad and if there is a better solution to making the methods non static (other than just removing the keyword) so that I can stub out these methods using the mocking framework?

Thanks!

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
odiggity
  • 4,117
  • 7
  • 34
  • 41
  • 2
    There's absolutely nothing wrong with a static method, if it's a "pure" function, or if it references only "system" stuff such that it has no local state. If it's referencing globals you need to tread a little more carefully, though. – Hot Licks Jan 17 '13 at 20:20
  • 1
    @HotLicks What are some examples of "system stuff"? Also, what are some examples of local state? Does the filesystem count as local state? Does a website? – Daniel Kaplan Jan 18 '13 at 00:46
  • 1
    "System stuff" might be, eg, retrieving process storage statistics or fetching the time of day or some such. "Local state" implies that the method is stashing data in a `static` or some such -- it's really an object pretending not to be. – Hot Licks Jan 18 '13 at 01:16

5 Answers5

9

The problem with static methods is they're very hard to fake when they're not relevant to the system you're trying to test. Imagine this code:

public void systemUnderTest() {
    Log.connectToDatabaseForAuditing();
    doLogicYouWantToTest();
}

The connectToDatabaseForAuditing() method is static. You don't care what this method does for the test you want to write. But, to test this code now you need an available database.

If it were not static the code would look like this:

private Logger log; //instantiate in a setter AKA dependency injection/inversion of control

public void systemUnderTest() {
    log.connectToDatabaseForAuditing();
    doLogicYouWantToTest();
}

And your test would be trivial to write without a database now:

@Before
public void setUp() {
    YourClass yourClass = new YourClass();
    yourClass.setLog(new NoOpLogger());

}

//.. your tests

Imagine trying to do that when the method is static. I can't really think of a way except for modifying the logger to have a static variable called inTestMode that you set to true in the setUp() to make sure it doesn't connect to a database.

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 2
    I understand from a mocking standpoint why it is necessary to not have static methods, but I'm more or less trying to figure out why a lot of people are saying using static methods is bad programming practice (e.g. http://blogs.msdn.com/b/nickmalik/archive/2005/09/06/461404.aspx). Perhaps though this is a good enough reason, all code should be testable.. – odiggity Jan 17 '13 at 20:41
  • 1
    I think I'm in the same boat as you. The problem I have with the SOLID principles is they explain *what* but not *why*. One thing I find interesting is you'd have the **exact same problem** and the **exact same solution** that I described above if you were asking this question about the Singleton pattern. Maybe that's more than a coincidence. – Daniel Kaplan Jan 17 '13 at 23:25
  • In reality, whether it is easy, hard, or impossible to fake or mock a static method depends entirely on the mocking tool you are using. With Mockito, it's not possible. With Mockito+PowerMock it's possible, though not exactly easy (due to the Mockito and PowerMock APIs been two separate APIs, and also due to certain requirements for using the PowerMock API). With JMockit (my own tool), it's easy: just declare a `@Mocked ApiCaller mock` field/parameter and then record or verify expectations on any of the static methods in `ApiCaller` (though it there might be a better design in this case). – Rogério Jan 18 '13 at 11:30
2

It is less modular. Instead you should define an interface ApiCaller with an instance method makeHttpCall() so that you can define separate implementations in the future.

In the very least you will always have 2 implementations of an interface, the original and the mocked version.

(Note: there are some mocking frameworks that allow you to mock static methods)

As an addendum, while this may not be the case in your specific application, typically the use of static methods is indicative of a larger design oversight. Designing for modularity and reuseability should be prevalent throughout your application, because even though you don't need it right now you may need it in the future, and it's much harder and much more time consuming to change things after the fact.

Alex DiCarlo
  • 4,851
  • 18
  • 34
  • Although that's generally a good idea, you don't have to do that when using Mockito. You can mock the concrete class and fake the concrete method functionality. – Daniel Kaplan Jan 17 '13 at 20:34
  • 1
    I have heard this argument before but I'm not convinced. I can see how it would apply in some cases. For example a storage class that has generic functions like store, retrieve, update and it's implementation could realistically be swapped out with something else. However in my case, the ApiCaller is a very unique class and there is no logical reason I can think of why it would ever be 'swapped' out for another implementation of the interface. Some of the function's logic might be changed but not the whole thing and creating an interface seems like overkill. – odiggity Jan 17 '13 at 20:35
  • @DanielKaplan Yes, that's is what I was referring to when I said mocked version. Regardless of whether you hand write it or a framework generates it for you, it is still an *implementation* of your interface – Alex DiCarlo Jan 17 '13 at 20:35
  • @odiggity It's easy to think of a scenario where you'd want to swap it out: You're *unit* testing a class that uses `ApiCaller` and `ApiCaller`'s functionality is irrelevant to the unit test. – Daniel Kaplan Jan 17 '13 at 20:38
  • @odiggity Exactly what Daniel said is what I meant by you will always have at least 2 implementations. – Alex DiCarlo Jan 17 '13 at 20:39
  • @DanielKaplan touché, but then it seems that the argument to eliminate helper methods is more or less make it easier to test. This is kind of the same conclusion I've come to with your answer below. I was confused because this reason doesn't seem to come up the arguments I've seen elsewhere against helper methods. – odiggity Jan 17 '13 at 20:45
1

PRIVATE Static helper methods are not bad, in fact, they are actually preferred at the large corporation where I work. And I use Mockito with them all the time, accessed from the methods that call the static helper method.

There is a slight difference in how the compiler treats a static helper method. The byte code created will result in an invokestatic instruction, and if you remove the static the result will be one of the other instructions, like invokespecial. The difference there being that invokestatic loads the class to access the method, where invokespecial pops the object off the stack first. So there might be a slight performance advantage (maybe not).

MattC
  • 5,874
  • 1
  • 47
  • 40
0

That you can't mock them easily when you need to pretty much answers your own question.

Particularly when it's something as shown: making an HTTP call is expensive, and doing that for unit testing your code makes no sense–save that for integration testing.

Unit tests require known responses (and response codes) from HTTP calls, something that you can't do if you're calling someone else's service, using a network you don't control.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
0

This explanation seems to me very straight forward, and easy to understand.

Declare utility methods as static, often makes your code easier to understand, which is a good thing.

But, there is a serious limitation to this approach though: such methods / classes can't be easily mocked.

So if a helper method has any external dependency (e.g. a DB) which makes it - thus its callers - hard to unit test, it is better to declare it non-static.

This allows dependency injection, thus making the method's callers easier to unit test.

Source: https://softwareengineering.stackexchange.com/a/111940/204271

JRichardsz
  • 14,356
  • 6
  • 59
  • 94