views:

205

answers:

5

Let's say I have a class like the following:

public class Test{
        private RemoteDoc.Documentation docService = new RemoteDoc.Documentation();
        public Test(){}
}

So this makes it difficult to unit test because there is a dependency on the proxy class. You can pass in the object via the constructor like so:

public class Test{
        private RemoteDoc.Documentation docService;
        public Test(RemoteDoc.Documentation serv)
        {
               docService = serv;
        }
}

Now in my unit tests I can instantiate the Test class and pass in a mocked object into the constructor. However, this solution isn't ideal because now other classes have to know about RemoteDoc.Documentation proxy class and have explicit references to it. What's is a good solution to this problem?

EDIT: To be more clear, RemoteDoc.Documentation is a proxy class for a web reference. Think of it like if you were using salesforce.com's api and all you really have is the wsdl and disco files.

+3  A: 

Your proposed solution that involves passing in the dependency via the constructor is, in fact, ideal. It is a well-known Dependency Injection (DI) pattern known as Constructor Injection.

What at first seems like a weakness turns out to actually be a strength. While it is true that every single consumer of the Test class (in your example) must now supply some implementation of the proxy (I hereby assume that the proxy is an interface or abstract base class), they can supply any implementation of that abstraction and not only that which you originally had in mind. Congratulations: you have just opened your class for extensibility!

This still leaves the question about where do you actually place the responsibility of deciding which dependencies go where? You should do this in the root of the application in a place called a Composition Root. This is explained in more details over in this SO answer.

You can use a DI Container to Auto-Wire your depdencies. Some common DI Containers are:

Mark Seemann
I am aware of dependency injection and am using windsor in other parts of my application. However, RemoteDoc.Documentation is a proxy class for a web reference (for example, say I was consuming salesforce.com's web service). Therefore, it doesn't exist in an assembly, isn't an interface, and I can't modify it. Can you use dependency injection in this situation?
@aspnetuser: If RemoteDoc.Documentation is a proxy class, you will not be able to mock it, making most of your question moot. However, you could extract an interface of that class and program against that interface instead.
Mark Seemann
You could also wrap the proxy class and apply the interface on that if you really wanted. Either way some form of inject is the way to go as it gives you the most control.
smaclell
A: 

I second Mark's approach. For completeness another option is as follows:

public class Test
{        
     private RemoteDoc.Documentation docService;     

     // Constructor providing default for docService
     public Test()
     {
         docService = new RemoteDoc.Documentation();
     }   

     // Constructor for injection
     public Test(RemoteDoc.Documentation serv)       
     { 
          docService = serv;        
     }
}

This means you have a default implementation as well as having the option of plugging in a different implementation. Useful if you don't want to use a container.

I've used both approaches in the past. When developing non trivial software the DI container approach is usually a better way to go.

RichardOD
+2  A: 

I like RichardOD's approach. One refinement that is useful for unit testing is to use a mock object instead of accessing the real web service. This will mean your tests will be decoupled from any external services, and run faster.

You can do this if the code is changed to look like:

public class Test
{        
     private RemoteDoc.IDocumentation docService;     

     // Constructor providing default for docService
     public Test()
     {
         docService = new RemoteDoc.Documentation();
     }   

     // Constructor for injection
     public Test(RemoteDoc.IDocumentation serv)       
     { 
          docService = serv;        
     }
}

Then you create a mock documentation object using a mocking framework like:

... and pass it into the Test(RemoteDoc.IDocumentation serv) constructor.

Since RemoteDoc.Documentation is a concrete class, you can make it inherit from RemoteDoc.IDocumentation using a partial class:

namespace RemoteDoc
{
    public interface IDocumentation
    {
        // public functions you want to mock go here
        string GetDocumentation();
    }

    public partial class Documentation : IDocumentation {}
}
Phillip Ngan
Unfortunately, I can't modify the Documentation class since its a web reference (see my Edit). If I could modify it, I would have done this exact thing already and is the reason why I am unsure of what to do.
From your wsdl and disco classes, Visual Studio will generate a concrete proxy class. The auto generated code is usually contained in the file References.cs. Then in another file, you would put the code shown in the second excerpt. There is no need to modify the original proxy class code (in References.cs)
Phillip Ngan
The reason the code can be in a file other than References.cs is because of the 'partial' keyword in 'public partial class Documentation : IDocumentation'.
Phillip Ngan
A: 

Note that this is much easier using WCF, since a service contract is already an interface. Your mock class need only implement the interface.

John Saunders
A: 

How about this approach? You will have to write more code to support the functionality in the proxy class. But it will gives you the flexibility for unit testing.

public interface IDocumentation
{
    // Add whatever functionality you need from RemoteDoc.Documentation
}

public class RemoteDocumnetation : IDocumentation
{
    private RemoteDoc.Documentation docService = new RemoteDoc.Documentation();

    // Implements IDocumentation 
}

public class Test{
        private IDocumentation doc;
        public Test(IDocumentation serv)
        {
               doc= serv;
        }
}
weilin8