Unit Testing – Static Services and Testability

dependency-injectionobject-orientedunit testing

Where is the figurative line drawn for using static services in a project? I am a coop student working and learning how to write .net MVC projects. I've been developing trying to stick to TDD. In my project I'm using ninject for dependency injection. I have written an abstract and a class implementing that for making API service calls to our internal API. There is no class library for these APIs yet so I'm writing my own stuff.

In this case I'm finding myself wondering why I am using non-static classes for this API service. There is no state information stored for API calls they are IP validated. All the addresses for call locations are stored in the config file.

I can't see how using a static class here would make testability harder. You can unit test the static class to see that it works. If you find yourself needing mock data you could simply mock the object the API is supposed to return. If that is impossible because of your method design it is highly likely the method you are testing is trying to do to much.

I feel that almost all situations, in this particular case, where the fact the service is static would affect testing would arise when trying to manipulate the returned objects; which should be abstracted out to an extension or utility method that itself could be tested, so there should be no issue with the static class affecting testing.

Is there a good reason to not go static that I am just missing, or something about unit testing and injection that I am just missing? I would like to be able to confidently say using static is a sound idea in this case, but feel am I lacking the knowledge to do so.

Another question was linked 'Is static universally evil' that is not my question. I know static isn't universally evil and has its place. My question is where is it prudent to use static and under what circumstances might an abstract service be better written as a static one, while not causing testability issues.

Best Answer

Static methods are problematic because they prevent dependency injection for users of that static method.

First, let me be clear that this is not always an issue. If a function is pure (maintains no state across calls, and does not perform any I/O), then you can just test it directly once, and then use it in other code knowing that it will work. This usually only applies to small utility methods, or when you're doing strict functional programming.

If the service provided by that static method is more complex, using dependency injection techniques becomes desirable:

  • If a method maintains state, you do not want to keep that state between different tests – that would be a kind of coupling that could obscure bugs. At the very least, you will want a mechanism to (at least temporarily) reset the state for each test. The easiest way to do that is to create a new instance of the enclosing class, which is not possible with static methods and static classes.

  • If the service interacts with the outside world or performs any I/O (aside from logging), you don't want to do that during an unit test.

    Let's consider a service to send email notifications. The system under test should send emails under certain conditions. How do you test this? Set up a test email account, and verify that you got the expected message? Yes, but that would be an integration test. For unit tests, we would just want to make sure that the email service was invoked with the correct configuration.

    If you can intercept the call through some other mechanism than replacing the service through dependency injection, that's probably OK as well.

  • If the service contains substantial business logic that might change frequently, you do not want to have unrelated tests fail because of changes to that logic. In the scope of a test, replacing unrelated business logic with stubs will make your tests more robust – assuming it is of course tested elsewhere.

Static classes do have their uses, but they should not be your default choice. Only when it's clearly not desirable to have an instance of that class should you make it static, which pretty much only happens when it's less of a class and more of a namespace for static methods.