Skip to content

Mocking Asynchronous Pattern by Microsoft Research Moles

Akira Sugiura edited this page Oct 12, 2015 · 10 revisions

Let's migrate the sample that mocks with Moles against the class that embraces "Event-based Asynchronous Pattern" to Prig(with Moq). Search the keyword "ShowGoogle" on the page, and you will find the example.

Let's create a class ULWebClient which have the method ShowGoogle in the project MolesMigrationDemo, and create its test project MolesMigrationDemoTest:

using System;
using System.Net;

namespace MolesMigration
{
    public class ULWebClient
    {
        public static void ShowGoogle()
        {
            var client = new WebClient();
            client.DownloadStringCompleted += (sender, e) =>
            {
                Console.WriteLine(e.Result);
            };
            client.DownloadStringAsync(new Uri("http://google.co.jp/"));
        }
    }
}

Add the assembly reference NUnit to MolesMigrationDemoTest, then install Prig in accordance with README.md. Oh, don't forget adding Moq:

Select in order the context menus Add Prig Assembly for mscorlib(to enable mocking Console) and Add Prig Assembly(WebClient, DownloadStringCompletedEventArgs) in the following image:

By selecting these context menus, mscorlib.v4.0.30319.v4.0.0.0.prig and System.v4.0.30319.v4.0.0.0.prig are added to the test project:

Next, run the following commands. It means that copying the indirection setting for Console to the clipboard. So, paste it to mscorlib.v4.0.30319.v4.0.0.0.prig:

PM> pfind ([System.Console]) 'WriteLine\(System.String\)' | pget | clip

For your information, let me explain each alias, pfind is Find-IndirectionTarget and pget is Get-IndirectionStubSetting. It goes like this:

Similarly, create the settings for WebClient and DownloadStringCompletedEventArgs. Get-IndirectionStubSetting can make the Indirection Stub Setting that it think the best if you just pass an array of MethodBase, so it is no problem using the results that are taken from standard Reflection API and are filtered by PowerShell:

PM> $targets = [System.Net.WebClient].GetMembers([System.Reflection.BindingFlags]'Public, Instance') | ? { $_ -is [System.Reflection.MethodBase] } | ? { $_.ToString() -match 'DownloadString' }
PM> $targets += [System.Net.DownloadStringCompletedEventArgs].GetMembers([System.Reflection.BindingFlags]'Public, Instance') | ? { $_ -is [System.Reflection.MethodBase] } | ? { $_.ToString() -match 'Result' }
PM> $targets | pget | clip

Then, paste the results to System.v4.0.30319.v4.0.0.0.prig as follows:

Now, the build succeeded? If it is true, you can use the indirection stubs. You can migrate from the original example to the followings:

using MolesMigration;
using Moq;
using NUnit.Framework;
using System.Net;
using System.Net.Prig;
using System.Prig;
using Urasandesu.Prig.Delegates;
using Urasandesu.Prig.Framework;

namespace MolesMigrationTest
{
    [TestFixture]
    public class ULWebClientTest
    {
        [Test]
        public void ShowGoogle_should_write_response_from_google_to_standard_output()
        {
            // Prig has no attributes like HostType("Moles"). Use using (new IndirectionsContext()) instead of that.
            using (new IndirectionsContext())
            {
                // Arrange 
                var handler = default(DownloadStringCompletedEventHandler);
                handler = (sender, e) => { };

                // AllInstances that is the feature of Moles to mock all instance members doesn't exist, because it is default feature.
                PWebClient.AddDownloadStringCompletedDownloadStringCompletedEventHandler().Body = (@this, value) => handler += value;
                PWebClient.RemoveDownloadStringCompletedDownloadStringCompletedEventHandler().Body = (@this, value) => handler -= value;
                PWebClient.DownloadStringAsyncUri().Body = (@this, address) =>
                {
                    // Use the stub that starts with PProxy if you want to mock against one instance.
                    var e = new PProxyDownloadStringCompletedEventArgs();
                    e.ResultGet().Body = @this_ => "google!modoki";
                    handler(@this, e);
                };
                var mockWriteLine = new Mock<IndirectionAction<string>>();
                mockWriteLine.Setup(_ => _(It.IsAny<string>()));
                PConsole.WriteLineString().Body = mockWriteLine.Object;


                // Act 
                ULWebClient.ShowGoogle();


                // Assert 
                mockWriteLine.Verify(_ => _("google!modoki"), Times.Once());
            }
        }
    }
}

Complete source code is here.