How to mock with static methods?
Asked Answered
E

7

35

I'm new to mock objects, but I understand that I need to have my classes implement interfaces in order to mock them.

The problem I'm having is that in my data access layer, I want to have static methods, but I can't put a static method in an interface.

What's the best way around this? Should I just use instance methods (which seems wrong) or is there another solution?

Edmundson answered 30/9, 2008 at 13:38 Comment(0)
U
22

I would use a method object pattern. Have a static instance of this, and call it in the static method. It should be possible to subclass for testing, depending on your mocking framework.

i.e. in your class with the static method have:

private static final MethodObject methodObject = new MethodObject();

public static void doSomething(){
    methodObject.doSomething();
}

and your method object can be a very simple, easily-tested:

public class MethodObject {
    public void doSomething() {
        // do your thang
    }
}
Uncommunicative answered 30/9, 2008 at 13:41 Comment(5)
Do you have any good references for that pattern? I'm not sure how I'd go about doing that.Edmundson
Err, no actually. I'm not sure if I made that name up, or it's considered more an idiom than a pattern, but I can't see any decent references by Google. Will keep looking though.Uncommunicative
Thanks, the example helped me understand what you meant.Edmundson
Doing this actually kills all the idea about having static methods.Wayless
isn't this called singleton?Zeringue
S
29

Yes, you use instance methods. Static methods basically say, "There is one way to accomplish this functionality - it's not polymorphic." Mocking relies on polymorphism.

Now, if your static methods logically don't care about what implementation you're using, they might be able to take the interfaces as parameters, or perhaps work without interacting with state at all - but otherwise you should be using instances (and probably dependency injection to wire everything together).

Swarth answered 30/9, 2008 at 13:43 Comment(0)
F
28

I found a blog via google with some great examples on how to do this:

  1. Refactor class to be an instance class and implement an interface.

    You have already stated that you don't want to do this.

  2. Use a wrapper instance class with delegates for static classes members

    Doing this you can simulate a static interface via delegates.

  3. Use a wrapper instance class with protected members which call the static class

    This is probably the easiest to mock/manage without refactoring as it can just be inherited from and extended.

Foothill answered 30/9, 2008 at 13:59 Comment(1)
That was amazing thank you, the third option was perfect for mePongid
U
22

I would use a method object pattern. Have a static instance of this, and call it in the static method. It should be possible to subclass for testing, depending on your mocking framework.

i.e. in your class with the static method have:

private static final MethodObject methodObject = new MethodObject();

public static void doSomething(){
    methodObject.doSomething();
}

and your method object can be a very simple, easily-tested:

public class MethodObject {
    public void doSomething() {
        // do your thang
    }
}
Uncommunicative answered 30/9, 2008 at 13:41 Comment(5)
Do you have any good references for that pattern? I'm not sure how I'd go about doing that.Edmundson
Err, no actually. I'm not sure if I made that name up, or it's considered more an idiom than a pattern, but I can't see any decent references by Google. Will keep looking though.Uncommunicative
Thanks, the example helped me understand what you meant.Edmundson
Doing this actually kills all the idea about having static methods.Wayless
isn't this called singleton?Zeringue
C
5

You might be trying to test at too deep a starting point. A test does not need to be created to test each and every method individually; private and static methods should be tested by calling the public methods that then call the private and static ones in turn.

So lets say your code is like this:

public object GetData()
{
 object obj1 = GetDataFromWherever();
 object obj2 = TransformData(obj1);
 return obj2;
} 
private static object TransformData(object obj)
{
//Do whatever
}

You do not need to write a test against the TransformData method (and you can't). Instead write a test for the GetData method that tests the work done in TransformData.

Cruces answered 30/9, 2008 at 13:55 Comment(0)
I
4

Use instance methods where possible.

Use public static Func[T, U] (static function references that can be substituted for mock functions) where instance methods are not possible.

Ingenerate answered 30/9, 2008 at 13:44 Comment(0)
I
0

A simple solution is to allow to change the static class's implementation via a setter:

class ClassWithStatics {

  private IClassWithStaticsImpl implementation = new DefaultClassWithStaticsImpl();

  // Should only be invoked for testing purposes
  public static void overrideImplementation(IClassWithStaticsImpl implementation) {
     ClassWithStatics.implementation = implementation;
  }

  public static Foo someMethod() {
    return implementation.someMethod();
  }

}

So in the setup of your tests, you call overrideImplementation with some mocked interface. The benefit is that you don't need to change clients of your static class. The downside is that you probably will have a little duplicated code, because you'll have to repeat the methods of the static class and it's implementation. But some times the static methods can use a ligther interface which provide base funcionality.

Implication answered 30/9, 2008 at 14:13 Comment(1)
Just a small point, wouldn't implementation have to be declared static if it was to be accessed in a static way?Uncommunicative
O
0

The problem you have is when you're using 3rd party code and it's called from one of your methods. What we ended up doing is wrapping it in an object, and calling passing it in with dep inj, and then your unit test can mock 3rd party static method call the setter with it.

Overhappy answered 15/7, 2009 at 18:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.