The Light
The Light

Reputation: 27011

How to design for mockability when there is a dependency?

I have a method inside which it calls the .NET XDocument.Load() method to load the xml data from a url. I'd like to make my class unit testable.

So how to make that call mockable/unit testable?

private void ProcessData(string url)
{
            // todo: make this mockable
            var xDocument = XDocument.Load(url);
            // the rest of the code
}

One solution I've used is to inject an xmlUrlLoader into the clas like below:

private readonly Func<string, XDocument> _xmlUrlLoader;

public MyConstructor(Func<string,XDocument> xmlUrlLoader)
{
    _xmlUrlLoader = xmlUrlLoader;
}
private void ProcessData(string url)
{
            // todo: make this mockable
            var xDocument = this._xmlUrlLoader(url);
            // the rest of the code
}

Is there any better way?

Upvotes: 3

Views: 407

Answers (2)

Andy Nichols
Andy Nichols

Reputation: 3002

I agree that injecting a loader is the correct approach I'm not sure why it would be of type Func

I would expect an interface along the lines of

public interface IXmlDocumentLoader
{
    XDocument LoadDocument(string url);
}

and then your code would look like

private readonly IXmlDocumentLoader _xmlUrlLoader;

public MyConstructor(IXmlDocumentLoader xmlUrlLoader)
{
    _xmlUrlLoader = xmlUrlLoader;
}

private void ProcessData(string url)
{
            var xDocument = this._xmlUrlLoader(url);
            // the rest of the code
}

But I think that for reasons of separation of concerns the document loader should definitely be in its own class.

Upvotes: 3

CodesInChaos
CodesInChaos

Reputation: 108800

I prefer separating logic from accessing resources. In your case I'd pass the document to the processing function:

void ProcessData(XDocument xDocument);

You can test that function without mocking anything. Then if you want you can add a thin wrapper on top that does the loading.

void ProcessUrl(string url);
{
    var xDocument = XDocument.Load(url);
    ProcessData(xDocument);
}

You can use mocking to unit test the wrapper, but personally I don't see much gain in that. I prefer to exercise those wrappers only as part of integration tests.

Upvotes: 3

Related Questions