Login using System.Noticeably.Different.WebSite;
March 18, 2010 NoticeablyDifferent
Search for
 
Testing The Proxy Pattern 
    #region Code
public abstract class Subject {
    public abstract void Request();
}
 
public class RealSubject : Subject {
 
    public RealSubject() { }
 
    public override void Request() {
        // does something
    }
}
 
public class ProxySubject : Subject {
 
    private RealSubject realSubject = new RealSubject();
 
    public ProxySubject() { }
 
    public override void Request() {
        realSubject.Request();
    }
}
 
public class Client {
 
    public Client() { }
 
    public void MethodThatMakesProxyRequest() {
        new ProxySubject().Request();
    }
}
    #endregion
 
    #region Unit Tests
[TestFixture]
public class ProxySubjectTests {
 
    private Subject proxySubject = new ProxySubject();
 
    public ProxySubjectTests() { }
 
    [Test]
    public void RealProxyCallsRealSubjectWithoutThrowingAnException() {
        proxySubject.Request();
        //NOTE: This test does not need to assert anything.
        //It will throw an exception, thus causing the test to fail, if the RealSubject is changed.
    }
}
 
[TestFixture]
public class RealSubjectTests {
 
    private RealSubject realSubject = new RealSubject();
 
    public RealSubjectTests() { }
 
    [Test]
    public void RealSubjectDoesWhatIsExpectedWithinTheRequestMethod() {
        realSubject.Request();
        //Assert that the RealSubject performs its designated action appropriately.
    }
}
 
[TestFixture]
public class ClientTests {
 
    private Client client = new Client();
 
    public ClientTests() { }
 
    [Test]
    public void TestOfMethodThatMakesProxyCall() {
        client.MethodThatMakesProxyRequest();
        //Assert that the Client performs its designated action appropriately.
    }
}
    #endregion
 
 
I know that the classic GOF Proxy Pattern has the Subject as an abstract class implemented by both the RealSubject as well as the ProxySubject. You may not have control over the RealSubject and your ProxySubject may only implement a subset of the methods implemented in the RealSubject. In this sample I envision it to be equatable to creating a web service proxy (ProxySubject) to a third party web service (RealSubject).
Now.. take this sample to the next level...
 
The thing to note here is that there is the introduction of a ProxyFactory class. The reason for this is to implement looser coupling between the Client and RealSubject. To test the Client you replace the ProxySubject with a MockProxySubject in the ProxyFactory to remove the call through the ProxySubject to the RealSubject.
 
    #region Code
public interface IProxy {
    //NOTE: Marker interface to enable replacement of proxies by tests
}
 
public interface ISubject : IProxy {
    void Request();
}
 
public class RealSubject {
 
    public RealSubject() { }
 
    public void Request() {
        // does something
    }
}
 
public class ProxySubject : ISubject {
 
    private RealSubject realSubject = new RealSubject();
 
    public ProxySubject() { }
 
    public void Request() {
        realSubject.Request();
    }
}
 
public class ProxyFactory {
 
    private System.Collections.Hashtable proxyCollection = new System.Collections.Hashtable();
 
    public static readonly ProxyFactory Instance = new ProxyFactory();
 
    private ProxyFactory() {
        ResetProxies();
    }
 
    public void ReplaceProxy(System.Type type, IProxy proxy) {
        proxyCollection[type] = proxy;
    }
 
    private void ResetProxies() {
        proxyCollection.Clear();
        proxyCollection.Add(typeof(ISubject), new ProxySubject());
    }
 
    public void Reset() {
        ResetProxies();
    }
 
    public ISubject SubjectProxy {
        get { return (ISubject)proxyCollection[typeof(ISubject)]; }
    }
}
 
public class Client {
 
    public Client() { }
 
    public void MethodThatMakesProxyRequest() {
        ProxyFactory.Instance.SubjectProxy.Request();
    }
}
    #endregion
 
    #region Unit Tests
public class MockProxySubject : ISubject {
 
    public MockProxySubject() { }
 
    public void Request4() {
    }
}
 
[TestFixture]
public class ProxyFactoryTests {
 
    private ISubject proxy = new MockProxySubject();
 
    public ProxyFactoryTests() { }
 
    [SetUp]
    public void SetUp() {
        ProxyFactory.Instance.Reset();
    }
 
    [Test]
    public void RealProxyIsDefault() {
        Assert.AreEqual(typeof(ProxySubject), ProxyFactory.Instance.SubjectProxy.GetType());
    }
 
    [Test]
    public void ProxyReplacedAndReturned() {
        Assert.AreEqual(typeof(ProxySubject), ProxyFactory.Instance.SubjectProxy.GetType());
        ProxyFactory.Instance.ReplaceProxy(typeof(ISubject), new MockProxySubject());
        Assert.AreEqual(typeof(MockProxySubject), ProxyFactory.Instance.SubjectProxy.GetType());
        ProxyFactory.Instance.Reset();
        Assert.AreEqual(typeof(ProxySubject), ProxyFactory.Instance.SubjectProxy.GetType());
    }
}
 
[TestFixture]
public class ProxySubjectTests {
 
    private ISubject proxySubject = new ProxySubject();
 
    public ProxySubjectTests() { }
 
    [Test]
    public void RealProxyCallsRealSubjectWithoutThrowingAnException() {
        proxySubject.Request();
        //NOTE: This test does not need to assert anything.
        //It will throw an exception, thus causing the test to fail, if the RealSubject is changed.
    }
}
 
[TestFixture]
public class RealSubjectTests {
 
    private RealSubject realSubject = new RealSubject();
 
    public RealSubjectTests() { }
 
    [Test]
    public void RealSubjectDoesWhatIsExpectedWithinTheRequestMethod() {
        realSubject.Request();
        //Assert that the RealSubject performs its designated action appropriately.
    }
}
 
[TestFixture]
public class ClientTests {
 
    private Client client = new Client();
 
    public ClientTests() { }
 
    [SetUp]
    public void SetUp() {
        //NOTE: Now I can replace the proxy (which would cause a call to the RealSubject) with a mock,
        //alleviating that potentially expensive call.
        //This allows my test of the Client test to be more true to a 'Unit' test.
        ProxyFactory.Instance.ReplaceProxy(typeof(ISubject), new MockProxySubject());
    }
 
    [Test]
    public void TestOfMethodThatMakesProxyCall() {
        client.MethodThatMakesProxyRequest();
        //Assert that the Client performs its designated action appropriately.
 
        //NOTE: This is where I can now assert that the Client's method is able to
        //handle exceptions or malicious values returned by the RealSubject
        //(particularly if it was controlled by a third party), in addition to valid responses.
        //This is something I wasn't able to control and test for in the first example and what
        //makes this sample better from both a threat modeling and unit testing standpoint.
    }
}
    #endregion