Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shall we avoid writing static methods in our java code for better testability?

Tags:

java

static

I was prefer using static methods in my java code, since I think they are "functional""stateless" and has less side-effect. So there may be some helper classes and methods like this:

public class MyHelper {
    public static Set<String> array2set(String[] items) { ... }
    public static List<String> array2list(String[] items) { ...}
    public static String getContentOfUrl(String url) {
        // visit the url, and return the content of response
    }
}

public class MyApp {
    public void doSomething() {
        String[] myarray = new String[]{ "aa","bb"};
        Set<String> set = MyHelper.array2set(myarray);
        String content = MyHelper.getContentOfUrl("http://google.com");
    }
}

But my friend says we should avoid defining such static utility methods, since we call them directly in our code, it will be hard to mock them or test them if they have external dependencies. He thinks the code should be:

public class ArrayHelper {
    public Set<String> array2set(String[] items) { ... }
    public List<String> array2list(String[] items) { ...}
}
public class UrlHelper {
    public String getContentOfUrl(String url) {
        // visit the url, and return the content of response
    }
}

public class MyApp {
    private final ArrayHelper arrayHelper;
    private final UrlHelper urlHelper;
    public MyApp(ArrayHelper arrayHelper, UrlHelper urlHelper) {
        this.arrayHelper = arrayHelper;
        this.urlHelper = urlHelper;
    }
    public void doSomething() {
        String[] myarray = new String[]{ "aa","bb"};
        Set<String> set = arrayHelper.array2set(myarray);
        String content = urlHelper.getContentOfUrl("http://google.com");
    }
}

In this way, if we want to write unit tests for MyApp, we can just mock the ArrayHelper and UrlHelper and pass them to the constructor of MyApp.

I agree totally about the UrlHelper part of his opinion, since the origin static code make MyApp untestable.

But I have a little confused about the ArrayHelper part, since it doesn't depend on any external resources and the logic will be very simple. Shall we avoid using static methods at this case too?

And when to use static methods? Or just avoid using it as much as possible?


update:

We are using "TDD" in our development, so the testability of a class often is the most important concern for us.

And I just replace the word "functional" with "stateless" in the first sentence since the that's real what I meant.

like image 855
Freewind Avatar asked Nov 23 '13 14:11

Freewind


People also ask

Should you avoid static methods Java?

They avoid trouble. Use static methods as often as possible. That's because static methods can't access the object's attributes. Static methods aren't part of the object, so they don't have access to anything that belongs to the object.

Should I avoid static methods?

Static methods are bad for testability. Since static methods belong to the class and not a particular instance, mocking them becomes difficult and dangerous. Overriding a static method is not that simple for some languages.

Is it good to use static methods in Java?

A static method has two main purposes: For utility or helper methods that don't require any object state. Since there is no need to access instance variables, having static methods eliminates the need for the caller to instantiate the object just to call the method.

Why should we avoid using static for everything?

Static variables are generally considered bad because they represent global state and are therefore much more difficult to reason about. In particular, they break the assumptions of object-oriented programming.

Are static methods and variables useful in Java?

That said, there are a few instances where static methods and variables are useful. However, all of these are accessed solely by methods in the class that also contains the statics, and do not have dependencies on anything outside them. Although a very rare case, sometimes it is useful to store data across every class instance.

How to test a code that calls a static method?

3 Best Practices to Test a Code That Calls Static Methods 1 Solution 1: Use dependency injection. If you have access to the source of the static method and you can/allowed to refactor it, the best solution is to change the static ... 2 Solution 2: Wrap static call in an instance method. ... 3 Solution 3: Mock the static methods. ...

Is it bad to use static methods?

In short: Yes. There are many disadvantages and static methods should almost never be used. Static methods allow procedural/functional code to be shoe-horned into an Object Oriented world.

What is the difference between static and instance methods in Java?

Static methods vs Instance methods in Java. Instance method are methods which require an object of its class to be created before it can be called. To invoke a instance method, we have to create an Object of the class in within which it defined.


2 Answers

Just beware of one disease very common amongst Java "experts": overengineering.

In your specific example, you either do or don't have a mockability issue. If you had an issue, you wouldn't be asking general questions, therefore I conclude you don't have an issue at the moment.

The general argument is that static methods are simpler and therefore the preferred choice, whenever there is a choice. A would-be instance method must first prove itself of needing to be an instance method.

If this was my project, I would defer any makeovers into instance methods until such a moment where the need for that became clear and present.

like image 71
Marko Topolnik Avatar answered Sep 27 '22 19:09

Marko Topolnik


You'll probably never want to mock a method that converts an array to a list (or set), and this method doesn't need any state and doesn't depend on any environment, so a static method looks fine to me.

Just like the standard Arrays.asList() (which you should probably use).

On the other hand, accessing an external URL is typically the sort of thing that you want to be able to mock easily, because not mocking it would

  • make the test an integration test
  • require to have this external URL up every time you run your tests, which you probably can't guarantee
  • require to have this external URL return exactly what you want it to return in your test (including errors if you want to test the event of an error).
like image 26
JB Nizet Avatar answered Sep 27 '22 20:09

JB Nizet