Click here to Skip to main content
15,881,938 members
Articles / Programming Languages / C#

Using the Proxy Pattern to Inject the Uninjectable

Rate me:
Please Sign up or sign in to vote.
4.48/5 (8 votes)
15 Nov 2013CPOL5 min read 33.6K   90   24   9
If you're new to Dependency Injection, you will sometimes have a dependency that cannot be injected. This article covers these scenarios and outlines how the Proxy Pattern can be used to solve this problem.

Introduction

When I first discovered Dependency Injection, it changed the way I wrote code. It made Test Driven Development possible, coupled with mocking dependencies in unit tests. In those early days, I was often frustrated when I had a dependency that I couldn't inject, which means I couldn't mock it out in my unit tests. This article goes through three examples that I experienced and shows how the Proxy Pattern can be used as a solution to these scenarios.

Background

This article assumes you are already comfortable with dependency injection and how it works.

To learn more about the Proxy Pattern, see this article.

It's worth noting two points relating to this article:

  • Some descriptions of the Proxy Pattern talk about the real object sharing the same interface as the proxy object. I'm not doing that, as will be obvious in the scenarios below.
  • Many frameworks offer built-in solutions for the scenarios below, so you don't necessarily need to use the Proxy Pattern (in particular, the third scenario, string parameters in the constructor).

Scenarios Illustrating the Problem

Here are three different scenarios that I have run into in the past:

Can't Mock Static Methods

In one application I wrote, I needed to use the File object from the .NET framework to save and open files. File is a static class with static methods for Create and Open. I needed to stub these methods so that objects that were dependent upon File could be tested in isolation (e.g. run without needing to actually open and create a file). Mocking frameworks, like Rhino Mocks and MOQ, can't mock or stub a static method.

Can't Mock Extension Methods

I spent a lot of the past few years writing Silverlight and WPF applications, using the excellent PRISM framework to implement MVVM. One of the great features of this framework is the Navigation module. In my ViewModel tests, I wanted to assert that a navigation is called by RegionManager.RequestNavigate. Although RegionManager has an interface which can be mocked, RequestNavigate is an extension method. Mocking frameworks, like Rhino Mocks and MOQ, can't mock or stub an extension method.

Can't Inject Objects that Have Value Types or Strings Passed into the Constructor

A recent project involved writing a WPF application that accessed calendars using the Google Calendar service. The Google CalendarService has a single constructor which expects a string (used to name the application, but in my case, I just wanted it hard-coded as null). Some Dependency Injection frameworks don't know how to construct this class because there's no way to know what value the string should be set to. In this particular project, I was using SimpleInjector. Note, this is one example where most Dependency Injection frameworks can solve this, but I've listed it in this article to illustrate how the Proxy Pattern can also solve it.

Proxy Pattern Solutions

I've attached an example project with all the problems and solutions illustrated. To focus on the technique, I've used very trivial examples.

Static Methods

Using the following code as an example of a static class.

C#
public static class CannotInjectStatic
{
    public static bool GreaterThanFive(int number)
    {
        return number > 5;
    }
} 

The first step is to add an interface to expose the static method that you want to mock.

C#
public interface ICannotInjectStatic
{
    bool GreaterThanFive(int number);
} 

Now implement that interface as a proxy class for the real class.

C#
public class CannotInjectStaticProxy : ICannotInjectStatic
{
    public bool GreaterThanFive(int number)
    {
        return CannotInjectStatic.GreaterThanFive(number);
    }
} 

Now the new interface and the proxy class can be registered with the Dependency Injection container and injected in objects that have a dependency on the real class. The following example uses Unity as the Dependency Injection container.

C#
container.RegisterType<ICannotInjectStatic, CannotInjectStaticProxy>(); 

The proxy class can now be mocked, so your class using the proxy class can use a mock in unit tests for asserting that calls are correct.

C#
[Test]
public void DoSomethingInjected_calls_CannotInjectStatic()
{
    // system under test
    _something.DoSomethingInjected();
    // assertions
    _cannotInjectStaticProxy.AssertWasCalled(x => x.GreaterThanFive(Arg<int>.Is.Equal(6)));
} 

And the mock can also return fake responses for unit tests.

C#
[Test]
public void DoSomethingInjected_calls_CannotInjectStatic([Values(true, false)]bool isGreaterThanFive)
{
    // setup
    _cannotInjectStaticProxy.Expect(
      x => x.GreaterThanFive(Arg<int>.Is.Anything)).Return(isGreaterThanFive);
    // system under test
    var result = _something.DoSomethingInjected();
    // assertions
    Assert.AreEqual(isGreaterThanFive, result);
} 

Extension Methods

Using the following code as an example of a class and extension method for the class.

C#
public class WillBeExtended
{
    public string Name { get { return "Name"; } }
}

public static class CannotInjectExtension
{
    public static string NamePlus(this WillBeExtended willBeExtended)
    {
        return willBeExtended.Name + "Plus";
    }
} 

Once again, we create an interface, exposing the extension method(s) we want to be able to mock. We also need to put in non-extension methods and properties from the real class that you want to use in the proxy class.

C#
public interface IWillBeExtendedProxy
{
    string NamePlus();
    string Name { get; }
} 

And then we implement the interface in the proxy class.

C#
public class WillBeExtendedProxy : IWillBeExtendedProxy
{
    private readonly WillBeExtended _willBeExtended;
    public WillBeExtendedProxy()
    {
        _willBeExtended = new WillBeExtended();
    }
    
    public string NamePlus()
    {
        return _willBeExtended.NamePlus();
    }
    public string Name { get { return _willBeExtended.Name; } }
} 

And register it.

C#
container.RegisterType<IWillBeExtendedProxy, WillBeExtendedProxy>(); 

And mock it.

C#
[Test]
public void DoSomethingInjected_calls_WillBeExtended()
{
    // system under test
    _something.DoSomethingInjected();
    // assertions
    _willBeExtendedProxy.AssertWasCalled(x => x.NamePlus());
} 

String Parameter in Constructor

Note: As noted above, this solution should only be considered if your Dependency Injection framework does not solve it directly. Most do.

Using the following code as an example of a class with a constructor that has a string parameter.

C#
public class CannotInjectConstructor
{
    private readonly string _name;
    public CannotInjectConstructor(string name)
    {
        _name = name;
    }
    public string Name { get { return _name; } }
} 

Create an interface for all the methods and properties that you want to mock.

C#
public interface ICannotInjectConstructor
{
    string Name { get; }
} 

Create the proxy class, with the constructor parameter hard-coded to the value desired. Obviously this assumes that the constructor value will always be the same, which was the case with the real world example above.

C#
public class CannotInjectConstructorProxy : ICannotInjectConstructor
{
    private readonly CannotInjectConstructor _cannotInjectConstructor;
    public CannotInjectConstructorProxy()
    {
        _cannotInjectConstructor = new CannotInjectConstructor("name");
    }
    public string Name { get { return _cannotInjectConstructor.Name; } }
} 

And finally, register it.

C#
container.RegisterType<ICannotInjectConstructor, CannotInjectConstructorProxy>(); 

Points of Interest

The proxy interface should only expose the methods and properties you intend to use. For example, the File class has 50+ methods, but I only needed Open and Create, so the interface only had those methods.

The proxy interface method and property signatures should be exactly the same as the real class. Don't be tempted to add some logic to these interfaces or proxy classes. The proxy class cannot be tested in isolation, so its functionality should be exactly the same as the real class. This isn't an issue because the real class should be fully tested by the third party that wrote the code (e.g. we should assume Microsoft have thoroughly tested File).

Try to avoid creating proxy classes for your own classes. In my experience, this usually indicates there's a design issue (e.g. you've used an extension method to extend your own code - rather than creating a proxy, change the extension method to be a normal method). The only time I've broken this rule is when enhancing a legacy application where the original code could not be altered.

To see the above code in action, I've attached a solution illustrating each. I've also included the unit tests, showing how the dependencies can be mocked and stubbed.

History

  • 7 October 2013: Initial version
  • 8 October 2013: Added mocking examples and further clarified when scenario 3 might be used  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
GeneralMy vote of 4 Pin
Praveen Raghuvanshi21-May-14 0:39
professionalPraveen Raghuvanshi21-May-14 0:39 
QuestionCool Pin
Stefan Zvonar16-Nov-13 0:29
Stefan Zvonar16-Nov-13 0:29 
GeneralMy vote of 4 Pin
Paulo Zemek7-Oct-13 5:42
mvaPaulo Zemek7-Oct-13 5:42 
GeneralRe: My vote of 4 Pin
User 4687017-Oct-13 11:06
User 4687017-Oct-13 11:06 
GeneralRe: My vote of 4 Pin
Paulo Zemek7-Oct-13 15:35
mvaPaulo Zemek7-Oct-13 15:35 
GeneralRe: My vote of 4 Pin
User 4687018-Oct-13 13:16
User 4687018-Oct-13 13:16 
GeneralRe: My vote of 4 Pin
Paulo Zemek9-Oct-13 7:00
mvaPaulo Zemek9-Oct-13 7:00 
QuestionGenerally nice, but not sure about constructor one Pin
Sacha Barber7-Oct-13 2:56
Sacha Barber7-Oct-13 2:56 
AnswerRe: Generally nice, but not sure about constructor one Pin
User 4687017-Oct-13 11:07
User 4687017-Oct-13 11:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.