93

I have found that there are only 3 ways to unit test (mock/stub) dependencies that are static in C#.NET:

Given that two of these are not free and one has not hit release 1.0, mocking static stuff is not too easy.

Does that make static methods and such "evil" (in the unit testing sense)? And if so, why does resharper want me to make anything that can be static, static? (Assuming resharper is not also "evil".)

Clarification: I am talking about the scenario when you want to unit test a method and that method calls a static method in a different unit/class. By most definitions of unit testing, if you just let the method under test call the static method in the other unit/class then you are not unit testing, you are integration testing. (Useful, but not a unit test.)

Vaccano
  • 4,077
  • 5
  • 34
  • 39

11 Answers11

120

Looking at the other answers here, I think there might be some confusion between static methods that hold static state or cause side-effects (which sounds to me like a really bad idea), and static methods that merely return a value.

Static methods which hold no state and cause no side effects should be easily unit testable. In fact, I consider such methods a "poor-man's" form of functional programming; you hand the method an object or value, and it returns an object or value. Nothing more. I don't see how such methods would negatively affect unit testing at all.

Robert Harvey
  • 200,592
31

You seem to be confusing static data and static methods. Resharper, if I remember correctly, recommends making private methods within a class static if they can be made so - I believe this yields a small performance benefit. It doesn't recommend making "anything that can be" static!

There is nothing wrong with static methods and they are easy to test (so long as they don't change any static data). For instance, think of a Maths library, which is good candidate for a static class with static methods. If you have a (contrived) method like this:

public static long Square(int x)
{
    return x * x;
}

then this is eminently testable and has no side effects. You just check that when you pass in, say, 20 you get back 400. No problem.

RapPayne
  • 273
Dan Diplo
  • 3,920
20

If the real question here is "How do I test this code?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Then, just refactor the code and inject as usual the call to the static class like this:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
Sunny
  • 515
17

Statics aren't necessarily evil, but they can limit your options when it comes to unit-testing with fakes/mocks/stubs.

There are two general approaches to mocking.

The first one (traditional - implemented by RhinoMocks, Moq, NMock2; manual mocks and stubs are in this camp, too) relies on test seams and dependency injection. Suppose you're unit-testing some static code and it has dependencies. What happens often in the code designed this way is that statics create their own dependencies, inverting dependency inversion. You soon discover that you can't inject mocked interfaces into code under test that is designed this way.

The second one (mock anything - implemented by TypeMock, JustMock and Moles) relies on .NET's Profiling API. It can intercept any of your CIL instructions and replace a chunk of your code with a fake. This allows TypeMock and other products in this camp to mock anything: statics, sealed classes, private methods - things not designed to be testable.

There's an ongoing debate between two schools of thought. One says, follow SOLID principles and design for testability (that often includes going easy on statics). The other one says, buy TypeMock and don't worry.

azheglov
  • 7,185
14

Check this out: "Static Methods are Death to Testability". Short summary of the argument:

To unit test you need to take a small piece of your code, rewire its dependencies and test it in isolation. This is hard with static methods, not only in the case they access global state but even if they just call other static methods.

Rafał Dowgird
  • 349
  • 1
  • 7
6

The simple truth rarely acknowledged is that if a class contains a compiler-visible dependency on another class, it cannot be tested in isolation from that class. You can fake up something that looks like a test, and will appear on a report as if it was a test.

But it will not have the key defining properties of a test; failing when things are wrong, passing when they are right.

This applies to any static calls, constructor calls, and any reference to methods or fields not inherited from a base class or interface. If the class name appears in the code, it is a compiler-visible dependency, and you can't validly test without it. Any smaller chunk simply is not a valid testable unit. Any attempt to treat it as if it were will have results no more meaningful than writing a little utility to emit the XML used by your test framework to say 'test passed'.

Given that, there are three options:

  1. define unit testing to be testing of the unit composed of a class and it's hard-coded dependencies. This works, providing you avoid circular dependencies.

  2. never create compile-time dependencies between classes you are responsible for testing. This works, providing you don't mind the resulting code style.

  3. don't unit test, integration test instead. Which works, providing it doesn't conflict with something else you need to use the term integration testing for.

soru
  • 3,655
4

There's no two ways about it. ReSharper's suggestions and several useful features of C# would not be used as often if you were writing isolated atomic unit tests for all your code.

For instance, if you have a static method and you need to stub it out, you can't unless you use a profile based isolation framework. A call-compatible workaround is to change the top of the method to use lambda notation. For example:

BEFORE:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

AFTER:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

The two are call-compatible. Callers don't have to change. The body of the function remains the same.

Then in your Unit-Test code, you can stub this call like so (assuming it's in a class called Database) :

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Be careful to replace it with the original value after you're done. You can do that via a try/finally or, in your unit-test clean up, the one that gets called after every test, write code such as this:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

which will re-invoke the static initializer of your class.

Lambda Funcs are not as rich in support as regular static methods, so this approach has the following undesirable side-effects:

  1. If the static method was an extension method, you have to change it to a non-extension method first. Resharper can do this for you automatically.
  2. If any of the data types of the static methods are an embedded-interop assembly, such as for Office, you have to wrap the method, wrap the type or change it to type 'object'.
  3. You can no longer use Resharper's change-signature refactoring tool.

But let's say you avoid statics altogether, and you convert this to an instance method. It's still not mockable unless the method is either virtual or implemented as part of an interface.

So in reality, anyone who suggests the remedy to stubbing static methods is to make them instance methods, they would also be against instance methods that are not virtual or part of an interface.

So why does C# have static methods? Why does it allow for non-virtual instance methods?

If you use either of these "Features", then you simply cannot create isolated methods.

So when do you use them?

Use them for any code that you don't expect anyone to ever want to stub out. Some examples: the Format() method of the String class the WriteLine() method of the Console class the Cosh() method of the Math class

And one more thing.. Most people won't care about this, but if you can about the performance of an indirect call, that's another reason to avoid instance methods. There are cases when it's a performance hit. That's why non-virtual methods exist in the first place.

zumalifeguard
  • 342
  • 1
  • 10
3

I see that after long time no one has yet stated a really simple fact. If resharper tells me that I can make a method static, it means a huge thing to me, I can hear his voice tell me: "hey, you, these pieces of logic are not current class's RESPONSIBILITY to handle, so it should stay out in some helper class or something".

g1ga
  • 139
3
  1. I believe it's partly because static methods are "faster" to call than instance methods. (In quotes because this smells of micro optimization) see http://dotnetperls.com/static-method
  2. It's telling you that it doesn't need state, therefore could be called from anywhere, removing the instatiation overhead if that's the only thing someone needs.
  3. If I want to mock it, then I think it's generally the practice that it's declared on an interface.
  4. If it's declared on an interface then R# won't suggest you make it static.
  5. If it's declared virtual, then R# won't suggest you make it static either.
  6. Holding state (fields) statically is something that should always be considered carefully. Static state and threads mix like lithium and water.

R# isn't the only tool that will make this suggestion. FxCop/MS Code Analysis will also do the same.

I would generally say that if the method is static, generally it should be testable as is as well. That brings some design consideration and probably more discussion than I have in my fingers right now, so patiently awaiting the down votes and comments... ;)

MIA
  • 5,214
2

If the static method is called from inside other method, it is not possible to prevent or substitute such a call. This means, these two methods make a single Unit; Unit test of any kind tests them both.

And if this static method talks to Internet, connects databases, shows GUI popups or otherwise converts the Unit test into complete mess, it just does without any easy work around. A method that calls such the static method is not testable without refactoring, even if it has lots of purely computational code that would highly benefit from being unit tested.

h22
  • 965
0

I believe that Resharper is giving you guidence and apply the coding guidelines it has been setup with. When I have used Resharper and it has told me that a method should be static it is bound to be on a private method that does not act on any instance variables.

Now as for testablity this scenario shouldn't be an issue as you shouldn't test private methods anyway.

As for the testability of static methods that are public then unit testing does become hard when static methods touch static state. Personally I would keep this to a minimum and use static methods as pure functions as much as possible where any dependencies are passed into the method which can be controlled via a test fixture. However this is a design decision.

aqwert
  • 499