views:

156

answers:

3

Our team has just started unittesting and mocking, and we have run into some discussion considering extension methods. The question is what is a good approach to testing classes, that make use of extension methods. I.e. we have an Enum like this..

public enum State
{
    [LangID(2817)]
    Draft = 0,
    [LangID(2832)]
    Booked = 1,
    [LangID(1957)]
    Overdue = 2,
    [LangID(2834)]
    Checked = 3,
}

Which makes use of the extension method:

public static string GetDescription(this Enum _enum)
{
    Type type = _enum.GetType();
    MemberInfo[] memInfo = type.GetMember(_enum.ToString()); 
    if (memInfo != null && memInfo.Length > 0)
    {
        object[] attrs = memInfo[0].GetCustomAttributes(typeof(LangID), false);
        if (attrs != null && attrs.Length > 0)
            return LanguageDB.GetString(((LangID)attrs[0]).ID);
    }
    return _enum.ToString();
}

Which again will be called by the class under test, like so ..

public class SUT(){

  public void MethodUnderTest(){
      string description = SomeObject.Status.GetDescription();//Status is Type State
  }
}

In this example the enum is getting a description in the language of the user, via LanguageDB, which unfortunately is not injected inside the class since it's static. We could naturally refractor the lot, but this would be a big investment, considering the code is working almost flawless. Any good suggestion?

A: 

almost flawless? ;-)

Rule 1: If it can't be tested don't write it.

Rule 2: Many things is life look like tehy are going to hurt, but don't.

How sure are you that this refactoring is going to be a big investment?

Rule 0: Rules are for the guidance of Wise Men and ond the obedience of Idiots.

This is a judgement call how likely are you avoid a defect in that method by the work? My guess is that in this case the benefits of refactoring that method are quite small.

djna
+2  A: 

If you're using MS' test suite you can do a very simple refactoring and be able to use Accessors to inject a mock into your static type object.

Let's say you've got code like this:

public static void Save(this Entity data)
{
  Repository.Instance.Save(data);
}

A static within a static... hard to test? Not really. Modify the extension class thusly:

private static Repository Instance
{
  get
  {
    return _repository ?? Repository.Instance;
  }
}
private static Repository _repository = null;

public static void Save(this Entity data)
{
  Instance.Save(data);
}

Simple refactor. Now, you can use the accessor to set your mock at test time...

[TestInitialize(), DebuggerStepThrough]
public void Setup()
{
  MyEntityExtensions_Accessor._repository = new Mock<IRepository>();
}
Will
Good answer, we where considering typemocks. But this solution would actually solve 95% of our issues. without major refractoring, thx
BBorg
If somebody balks at the null check you can simplify it further to do it only one time: **return _repository ?? (_repository = Repository.Instance);**
Will
One more thing... if you do the above (one time null check) you will have to set it once and then reuse the mock (you'll only be able to do this once per appdomain). You might have to wrap the mock with a type extending Repository in order to be able to set a new mock instance every time
Will
One last thing, you can send me a check for the cash you'd have spent on TypeMock. I'll split it with ya!
Will
A: 

One idea for testing the extension method itself would be to have a private method that does all the work that does get the LanguageDB injected. The public method would call the private method with the static LanguageDB, but you would actually test the private method via reflection and pass in a mock object.

As for testing the actual class under test, you'll probably need to come up with a way to replace the static LanguageDB -- perhaps just null it out and test that you are getting the actual enum names. After all, you've already tested that the extension works elsewhere so you don't really need to test it again in this class.

Ultimately, you're finding out that static objects are hard to test. The real answer, as you've already guessed, is to refactor to a better design that doesn't rely on static objects if they can be avoided (though this does seem to be a good candidate). Perhaps, you can get by just refactoring to include injection.

tvanfosson
yeah, refractoring seems to be the WTG.
BBorg