views:

225

answers:

3

All,

Wanted to get a few thoughts on this. Lately I am becoming more and more of a subscriber of "purist" DI/IOC principles when designing/developing. Part of this (a big part) involves making sure there is little coupling between my classes, and that their dependencies are resolved via the constructor (there are certainly other ways of managing this, but you get the idea).

My basic premise is that extension methods violate the principles of DI/IOC.

I created the following extension method that I use to ensure that the strings inserted into database tables are truncated to the right size:

public static class StringExtensions
{
    public static string TruncateToSize(this string input, int maxLength)
    {
        int lengthToUse = maxLength;
        if (input.Length < maxLength)
        {
            lengthToUse = input.Length;
        }

        return input.Substring(0, lengthToUse);
    }
}

I can then call my string from within another class like so:

string myString = "myValue.TruncateThisPartPlease.";
myString.TruncateToSize(8);

A fair translation of this without using an extension method would be:

string myString = "myValue.TruncateThisPartPlease.";
StaticStringUtil.TruncateToSize(myString, 8);

Any class that uses either of the above examples could not be tested independently of the class that contains the TruncateToSize method (TypeMock aside). If I were not using an extension method, and I did not want to create a static dependency, it would look more like:

string myString = "myValue.TruncateThisPartPlease.";
_stringUtil.TruncateToSize(myString, 8);

In the last example, the _stringUtil dependency would be resolved via the constructor and the class could be tested with no dependency on the actual TruncateToSize method's class (it could be easily mocked).

From my perspective, the first two examples rely on static dependencies (one explicit, one hidden), while the second inverts the dependency and provides reduced coupling and better testability.

So does the use of extension methods conflict with DI/IOC principles? If you're a subscriber of IOC methodology, do you avoid using extension methods?

+9  A: 

I see where you are coming from, however, if you are trying to mock out the functionality of an extension method, I believe you are using them incorrectly. Extension methods should be used to perform a task that would simply be inconvenient syntactically without them. Your TruncateToLength is a good example.

Testing TruncateToLength would not involve mocking it out, it would simply involve the creation of a few strings and testing that the method actually returned the proper value.

On the other hand, if you have code in your data layer contained in extension methods that is accessing your data store, then yes, you have a problem and testing is going to become an issue.

I typically only use extension methods in order to provide syntactic sugar for small, simple operations.

LorenVS
+10  A: 

I think it's fine - because it's not like TruncateToSize is a realistically replaceable component. It's a method which will only ever need to do a single thing.

You don't need to be able to mock out everything - just services which either disrupt unit testing (file access etc) or ones which you want to test in terms of genuine dependencies. If you were using it to perform authentication or something like that, it would be a very different matter... but just doing a straight string operation which has absolutely no configurability, different implementation options etc - there's no point in viewing that as a dependency in the normal sense.

To put it another way: if TruncateToSize were a genuine member of String, would you even think twice about using it? Do you try to mock out integer arithmetic as well, introducing IInt32Adder etc? Of course not. This is just the same, it's only that you happen to be supplying the implementating. Unit test the heck out of TruncateToSize and don't worry about it.

Jon Skeet
Same idea as my post, agreed
LorenVS
So, I don't disagree with your answer--it's very pragmatic. You can definitely take IOC (or any design theory) too far.For the sake of argument: assuming you are committed to IOC, you would have no problem with the second implementation example (the explicit dependency on a static class)? I have never read an article by a hardcore advocate of IOC that said static dependencies are acceptable.
Phil Sandler
A: 

Its a pain because they are hard to mock. I usually use one of these strategies

  1. Yep, scrap the extension its a PITA to mock out
  2. Use the extension and just test that it did the right thing. i.e. pass data into the truncate and check it got truncated
  3. If it's not some trivial thing, and I HAVE to mock it, I'll make my extension class have a setter for the service it uses, and set that in the test code.

i.e.

static class TruncateExtensions{

    public ITruncateService Service {private get;set;}
    public string TruncateToSize(string s, int size)
    {
         return (Service ?? Service = new MyDefaultTranslationServiceImpl()). TruncateToSize(s, size);
    }
}

This is a bit scary because someone might set the service when they shouldn't, but I'm a little cavalier sometimes, and if it was really important, I could do something clever with #if TEST flags, or the ServiceLocator pattern to avoid the setter being used in production.

mcintyre321