Geeks With Blogs
Patrice Calve Life's short, have fun

Hi there,

How do you mock an asynchronous web method (web service) call?  You can skip the background and move on directly to the solution...

Background:

In a CAB/SCSF project I'm working on, I'm testing a Presenter's method "OnViewReady".  In my implementation, this OnViewReady does basically 2 things:

  1. Tells the View to Show a "Loading..." message to the user
  2. Issues an asynchronous call to a Web Service

When the call is returned from the Web Service, the Presenter tells the View to Show the message returned by the Web Service. 

The method looks like this: 

Solution

The solution is threefold:

  1. Make the Web Service Mocking Friendly ...
  2. Make the presenter "interface friendly" ...
  3. Fine tune the Test Method! :) ...

Web Service Mocking friendly

Making the Web Service Mocking friendly was not so evident.  Well, at least, the "mocking async methods" part.

To mock a Web Service, you have to extract an interface from the generated web service code (reference.cs), and make the web service code inherit from that interface.  You could make the web service "reference.cs" implement the interface, but may kill that code when you "update web reference".  The trick is to add a second "partial class" that will inherit from that interface.

Here goes:

  • In Visual Studio,
  • In your MyAppModule's project, click Add Web Reference and follow the wizard
  • Open the Reference.cs in the IDE (Show All Files)
  • Right Click on the public partial class MyWebService and choose "Refactor -> Extract interface"
  • This will create a IMyWebService interface with all of the methods from you Web Service.
  • Now, add a new class "MyWebService", make sure the namespace and class declaration is the same as the "real" web service.  "public partial class MyWebService", but make it implement the interface you created.

This is the web service's signature (in my example, the "MyWebService" is called "GeneralWS")

public partial class GeneralWS : System.Web.Services.Protocols.SoapHttpClientProtocol { .. }So nothing changed.

 

This is the interface extracted:

    public interface IGeneralWS

    {

        void CancelAsync(object userState);

        string Ping();

        void PingAsync(object userState);

        void PingAsync();

        event PingCompletedEventHandler PingCompleted;

        string Url { get; set; }

        bool UseDefaultCredentials { get; set; }

    }

This is the second partial class.  Note that there's no code, just the interface:

    public partial class GeneralWS : IGeneralWS

    {

        //look ma, no hands

    }

Now, if you open the reference.cs, check the PingCompletedEventArgs code: The constructor is "internral" !   So, this means that we can't mock the async method because we can't create a new PingCompletedEventArgs when we mimic the callback.

Again, what you do, is create an interface and leverage the "partial" declaration of the PingCompletedEventArgs.  Repeat the steps for the Web Service class, but this time, for the EventArgs.

Here are my results:

    public interface IPingCompletedEventArgs

    {

        string Result { get; }

    }

Add the second "partial" class:

    public partial class PingCompletedEventArgs : IPingCompletedEventArgs

    {

        public PingCompletedEventArgs(string results) :

                base(null, false, null)

        {

            //We're hard coding the results to an 1-item array (object) since we know

            //that the web service's Ping method returns a string.

            this.results = new object[1];

            this.results[0] = results;

        }

    }

Ok, that takes care of the "Web Service" part.  At this point, you can run the solution and it will work.  And if you update the web reference, you won't loose a thing.

Making the presenter "interface friendly"

Making the presenter "interface friendly" is the well-known "multiple constructor" algorithm.

In a classic web service call, your method will be like this:

        private void doSomethingWild()

        {

            GeneralWS gws = new GeneralWS();

            string returnValue = gws.Ping();

            View.ShowMessage(returnValue);

        }

In a "mocking friendly" class, you can't do that.  The class must work with an interface instead and the mocking framework will "mock" that interface for you.

4 steps:

  1. Add a private variable of type Interface (that you created earlier)
  2. Add a default constructor that will attach a "real web service" to that private interface variable
  3. Add a "mockig friendly" constructor where pass in in the mocked interface (this is the real trick)
  4. Change the call to the web service to not create a reference to the "real web service", but simply re-use the private variable of type interface

here's a sample from my presenter (construction part):

    public partial class HelloWorldViewPresenter : Presenter<IHelloWorldView>

    {

        private PeopleCentralGeneral.IGeneralWS _gws;

 

        //default parameter-less Presenter, for normal code execution

        public HelloWorldViewPresenter()

        {

            _gws = new GeneralWS();

            _gws.PingCompleted += new PingCompletedEventHandler(gws_PingCompleted);

 

        }

        //special constructor for "Mocking Friendlyness"

        public HelloWorldViewPresenter(IGeneralWS gws)

        {

            _gws = gws;

            _gws.PingCompleted += new PingCompletedEventHandler(gws_PingCompleted);

 

        }

        ...

        /// <summary>

        /// This method is a placeholder that will be called by the view when it has been loaded.

        /// </summary>

        public override void OnViewReady()

        {

            View.ShowMessage("Loading...");

            base.OnViewReady();

 

            pingAsync();

 

        }

        private void pingAsync()

        {

            //note that I'm not creating a new reference to the web service but re-using the interace instead..

            //proper coding would check that _gws isn't null, of course ;)

            _gws.PingAsync();

        }

 

        private void gws_PingCompleted(object sender, PingCompletedEventArgs e)

        {

            View.ShowMessage(e.Result);

        }

        ...

}

 

 

Fine tuning the Test Method

Fine tuning the Test Method for async web methods requires another trick with the Web Service.

Finally, the Test Method !

In the HelloWorldViewPresenterTest.cs, here's the Test Method for the OnViewReady:

 

        /// <summary>

        ///A test for OnViewReady ()

        ///</summary>

        [TestMethod()]

        public void OnViewReadyTest()

        {

            MockRepository repo = new MockRepository();

 

            //the mocked general web service

            IGeneralWS genWS = repo.StrictMock<IGeneralWS>();

 

            //the mocked view

            IHelloWorldView view = repo.StrictMock<IHelloWorldView>();

            Support.TestableRootWorkItem workitem = new Support.TestableRootWorkItem();

 

            //this will be used to simulate the call back from the web service

            Rhino.Mocks.Interfaces.IEventRaiser pingCompletedRaiser;

 

            using (repo.Record())

            {

                Expect

                    .Call(delegate { view.ShowMessage("Loading..."); });

 

                //Expect that the PingAsynch() method will be called

                Expect

                    .Call(delegate { genWS.PingAsync(); });

 

                //Provide the entry point for the call back.

                genWS.PingCompleted += null;

 

                pingCompletedRaiser = LastCall

                    .IgnoreArguments()

                    .GetEventRaiser();

 

                Expect

                    .Call(delegate { view.ShowMessage("Pong"); });

            }

 

            HelloWorldViewPresenter target = new HelloWorldViewPresenter(genWS);

            target.View = view;

            target.WorkItem = workitem;

 

            using (repo.Playback())

            {

                target.OnViewReady();

 

                //This "new EventArgs" would be impossible without the second partial class

                PingCompletedEventArgs args = new PingCompletedEventArgs("Pong");

 

                //make the callback call....  

                pingCompletedRaiser.Raise(genWS, args);

 

                //we're done!

            }

        }

I hope this info will help you in your quest for building better applications. 

Patrice Calvé

 

Posted on Wednesday, October 15, 2008 8:35 AM P&P Software Factories | Back to top


Comments on this post: CAB/SCSF Mocking Asynchronous Web Methods

# re: CAB/SCSF Mocking Asynchronous Web Methods
Requesting Gravatar...
Hi Rajita,

Diclaimers... :)

First, the best place to ask this question is http://stackoverflow.com/. It's a great site for exchanging questions/ideas just like yours and where you'll find more intelligent and knowledgeable people than me :)

Second, I haven't done much cab/scsf/unit testing in the last 6 months and it's amazing how fast we loose our knowledge, sorry.

Third, I started to use MOQ (http://code.google.com/p/moq/) and found it more intuitive to write than rhino (but both are top notch frameworks!!!!)

Now, to try answering your question, the Rhino.Mocks.Interfaces.IEventRaiser and GetEventRaiser() method would be key in your test.
Left by Pat on May 15, 2009 9:22 AM

# re: CAB/SCSF Mocking Asynchronous Web Methods
Requesting Gravatar...
The "OnViewReady" method might save time in a support center for clients and it may reach to be a performance enhancer for every business of this kind. I guess this idea will be widely implemented in this business, on behalf of its revolutionary features.
Left by virus cleaner on Mar 19, 2011 10:21 AM

Your comment:
 (will show your gravatar)


Copyright © Patrice Calvé | Powered by: GeeksWithBlogs.net