ASP.net: Unit Testing with Web Service Proxy Class Dependency
Let's say I have a class like the following:
public class Test{
private RemoteDoc.Documentation docService = new RemoteDoc.Documentation();
public Test(){}
}
So it makes it difficult to unit test because there is a dependency on the proxy class. You can pass an object through the constructor like this:
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 the constructor to the project. However, this solution is not ideal because now other classes need to know about the RemoteDoc.Documentation proxy class and have explicit references to it. What's a good solution to this problem?
EDIT. To be more clear, RemoteDoc.Documentation is a proxy class for a web link. Think of it as if you were using the salesforce.com api and all you really have are the wsdl and disco files.
Your proposed solution, which involves passing the dependency through the constructor, is in fact ideal. This is a well known Dependency Injection (DI) pattern known as Constructor Injection .
At first, it seems that the weakness is actually strong. While it is true that every consumer of the Test class (in your example) must now provide some proxy implementation (I am thereby assuming that the proxy is an interface or abstract base class), they can provide any implementation of that abstraction and not just what you originally meant. Congratulations: you've just opened your class for extensibility !
This still leaves the question of where do you actually place the responsibility for deciding what dependencies go there? You have to do this at the root of the application, in a place called Roots . This is covered in more detail in this SO answer .
You can use the DI container to automatically mount your backups. Some common DI containers:
source to share
I like RichardOD's approach. One clarification that is useful for unit testing is to use a mock object instead of accessing a real web service. This will mean that your tests will be separate from any external services and will run faster.
You can do this if the code changes like this:
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 the layout of the documentation object using a fake framework, for example:
... and pass it to the Test constructor (RemoteDoc.IDocumentation serv).
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 {}
}
source to share
Mark's second approach. For completeness, another option looks like this:
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 the ability to plug 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 the best way to go.
source to share
How about this approach? You will need to write more code to support the functionality in the proxy class. But it will give you 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;
}
}
source to share