The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Request/Response Service Layer: Testing Synchronous Client-Side Usage

Posted by Davy Brion on November 13th, 2009

Note: This post is part of a series. Be sure to read the introduction here.

If you want to write automated tests for your client-side code, it’s often useful to replace the service proxy with a mock instance. With typical WCF services and their proxies, that’s pretty easy to do. With the Request/Response Service Layer (RRSL) and its IRequestDispatcher, it’s a bit more tricky. While you could provide a mock instance of IRequestDispatcher to your classes under test, we’ve learned that it’s easier to use a prepared stub class which inherits from the RequestDispatcher class and adds some extra methods to inspect the requests that were supposed to be sent, and to return response objects that you can easily prepare yourself.

If we go back to our implementation of the IRequestDispatcher interface, you’ll notice that the RequestDispatcher class has the following constructor:

        protected RequestDispatcher(IRequestProcessor requestProcessor)

        {

            this.requestProcessor = requestProcessor;

            InitializeState();

        }

As well as the following virtual method which is the only place where we actually use the IRequestProcessor to send requests to the Request Processor:

        protected virtual Response[] GetResponses(params Request[] requestsToProcess)

        {

            BeforeSendingRequests(requestsToProcess);

            return requestProcessor.Process(requestsToProcess);

        }

You’ll also notice that most of the public methods of the RequestDispatcher class are virtual and that many of its protected methods are virtual as well. While we don’t need to override all of them, there’s plenty of flexibility to do whatever you want to do. We’ll basically pass a null reference to the RequestDispatcher’s constructor (which takes an IRequestProcessor instance) and override the protected GetResponses method to simply return our prepared responses instead of actually sending them to the IRequestProcessor. We’ll also add a few methods so you can add prepared responses in your tests, as well as some methods which allow you to easily inspect whether certain requests were added by the code under test, and to retrieve the actual requests so you can verify that they contain the expected data.

There is no interface to define the added functionality of the stub, but this fictional interface might make it clearer what you’ll be able to do with the stub in your tests:

    public interface IRequestDispatcherStub : IRequestDispatcher

    {

        void AddResponsesToReturn(params Response[] responses);

        void AddResponsesToReturn(Dictionary<string, Response> keyedResponses);

        void AddResponseToReturn(Response response);

        void AddResponseToReturn(string key, Response response);

        TRequest GetRequest<TRequest>() where TRequest : Request;

        TRequest GetRequest<TRequest>(string key) where TRequest : Request;

        bool HasRequest<TRequest>() where TRequest : Request;

    }

Those added methods make it very clear to verify that your code under test is communicating with the RRSL in the way you intended it to.

So finally, this is the code of the RequestDispatcherStub class:

    public class RequestDispatcherStub : RequestDispatcher

    {

        private readonly List<Response> responsesToReturn = new List<Response>();

        private readonly Dictionary<string, Request> keyToRequest = new Dictionary<string, Request>();

 

        public RequestDispatcherStub() : base(null) { }

 

        public void AddResponsesToReturn(params Response[] responses)

        {

            responsesToReturn.AddRange(responses);

        }

 

        public void AddResponsesToReturn(Dictionary<string, Response> keyedResponses)

        {

            responsesToReturn.AddRange(keyedResponses.Values);

 

            for (int i = 0; i < keyedResponses.Keys.Count; i++)

            {

                var key = keyedResponses.Keys.ElementAt(i);

 

                if (key != null)

                {

                    keyToResultPositions.Add(key, i);

                }

            }

        }

 

        public void AddResponseToReturn(Response response)

        {

            responsesToReturn.Add(response);

        }

 

        public void AddResponseToReturn(string key, Response response)

        {

            responsesToReturn.Add(response);

            keyToResultPositions.Add(key, responsesToReturn.Count - 1);

        }

 

        public override void Clear()

        {

            // this Stub can't clear the state because we have to be able to inspect the sent requests

            // during our tests

        }

 

        public override void Add(string key, Request request)

        {

            base.Add(key, request);

            keyToRequest[key] = request;

        }

 

        public TRequest GetRequest<TRequest>() where TRequest : Request

        {

            return (TRequest)SentRequests.First(r => r.GetType().Equals(typeof(TRequest)));

        }

 

        public TRequest GetRequest<TRequest>(string key) where TRequest : Request

        {

            return (TRequest)keyToRequest[key];

        }

 

        public bool HasRequest<TRequest>() where TRequest : Request

        {

            return SentRequests.Count(r => r.GetType().Equals(typeof(TRequest))) > 0;

        }

 

        protected override Response[] GetResponses(params Request[] requestsToProcess)

        {

            return responsesToReturn.ToArray();

        }

 

        protected override void DealWithSecurityException(ExceptionInfo exceptionInfo)

        {

            throw new SecurityException("a security exception was thrown: " + exceptionInfo);

        }

 

        protected override void DealWithUnknownException(ExceptionInfo exceptionInfo)

        {

            throw new Exception("an unknown exception was thrown: " + exceptionInfo);

        }

    }

With this in place, you can very easily test whether the correct requests have been sent, whether they contain the expected data, and how your code reacts to the data in your prepared responses.

2 Responses to “Request/Response Service Layer: Testing Synchronous Client-Side Usage”

  1. Reflective Perspective - Chris Alcock » The Morning Brew #477 Says:

    [...] Request/Response Service Layer: Testing Synchronous Client-Side Usage – Davy Brion continues his Request Response series with a look at testing synchronious client side calls to the service layer, and looks at Asynchronous Client-Side Usage [...]

  2. Steve Says:

    r.GetType().Equals(typeof(TRequest))
    can be changed to
    r is TRequest

    Or you can use .OfType() instead.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>