Welcome to Atalasoft Community Sign in | Help

Mocking SPSecurity delegates

Since we’ve recently released VizitSP it’s time to start talking about more things SharePoint.  Quickly, I’m going to show you how we’ve used TypeMock’s Isolator for SharePoint along with NUnit and TestDriven.NET to test our use of SPSecurity’s static method RunWithElevatedPrivileges().  The main problem I found was that SPSecurity had to be mocked, otherwise it would throw a NullReferenceException.  Here’s an example:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4:  
   5: namespace Test
   6: {
   7:     [TestFixture]
   8:     public class Test
   9:     {
  10:         [Test]
  11:         [Isolated]
  12:         public void TestUsingSPSecurity()
  13:         {
  14:             SPSecurity.RunWithElevatedPrivileges(delegate()
  15:             {
  16:                 //
  17:             });
  18:         }
  19:     }
  20: }

In this example it would be thrown at line 14 for obvious reasons, there’s no SharePoint to test against!  Let’s flex those Mock muscles and handle calls to SharePoint’s static objects.

First, we need need to tell TypeMock that we’re going to Mock the SPSecurity object

using TypeMock;
...
public void TestUsing SPSecurity()
{
...
Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
...

Now, on the mocked SPSecurity object, we want to redirect calls to its RunWithElevatedPrivileges() function.  Start typing spSecMock.MockMethodCalled += and when you get to the “=” Visual Studio’s awesome code completion will step in:

image

Hit <tab> twice and you’ve got not only the whole method handler invocation but it’ll insert a stub handler for you.

Now we have to tell TypeMock to expect calls from RunWithElevatedPrivileges

spSecMock.ExpectAlways("RunWithElevatedPrivileges");

And we want to, in our method handler, only handle the RunWithElevatedPrivileges method calls, which we can get from the event args:

void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
{
    if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
        throw new System.NotImplementedException();
}

Just to show that it’s working, and we can actually mock SPSecurity, we could stop here and have NUnit expect the NotImplementedException:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4: using TypeMock;
   5:  
   6: namespace Test
   7: {
   8:     [TestFixture]
   9:     public class Test
  10:     {
  11:         [Test]
  12:         [Isolated]
  13:         [ExpectedException(typeof(System.NotImplementedException))]
  14:         public void TestUsingSPSecurity()
  15:         {
  16:             Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
  17:             spSecMock.MockMethodCalled += new MockMethodCalledEventHandler(spSecMock_MockMethodCalled);
  18:             spSecMock.ExpectAlways("RunWithElevatedPrivileges");
  19:  
  20:             SPSecurity.RunWithElevatedPrivileges(delegate()
  21:                 {
  22:                     //
  23:                 });
  24:         }
  25:  
  26:         void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
  27:         {
  28:             if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
  29:                 throw new System.NotImplementedException();
  30:         }
  31:     }
  32: }

Now we want the method handler to run the delegate code passed to RunWithElevatedPrivileges!  To do that we retrieve the delegate code from the event arguments and invoke it!  The whole method handler should look like this:

void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
{
    if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
    {
        SPSecurity.CodeToRunElevated del = (SPSecurity.CodeToRunElevated)e.SentArguments[0];
        IAsyncResult res = del.BeginInvoke(null, null);
        res.AsyncWaitHandle.WaitOne();
        del.EndInvoke(res);
    }
}
 
So, if we hand the method something to run, we can see it’s working.  Modify the interior of your anonymous delegate code, throwing the NotImplementedException there:
 
SPSecurity.RunWithElevatedPrivileges(delegate()
{
    throw new System.NotImplementedException();
});

Run this test, and it’ll still pass, meaning you’re properly calling into the delegate you passed to RunWithElevatedPrivileges()!

Finally, when you’re doing something elevated (and you’re not, are you?) TypeMock needs to step in there, too.  The easiest thing to do is ensure the next instance of some object is faked by TypeMock.  We want to work with some site we create inside the elevated code, and we need to mock that, too; here’s how:

   1: Guid siteGuid = Guid.NewGuid();
   2: SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
   3: Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
   4: Isolate.WhenCalled(() => fakeSite.ID).WillReturn(siteGuid);

You create a fake SPSite object using TypeMock’s Isolate.Fake.Instance() method.  Then, as you see on line 3, ensure that our mocked object is returned the next time an SPSite is constructed.  On line 4, we instruct TypeMock to return our local GUID, so we have some basis of comparison later on.  When you go to create a new site in your elevated code you can see that the returned ID is that of the fake constructed site:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    SPSite elevatedSite = new SPSite(siteGuid);
    Assert.AreEqual(elevatedSite.ID, fakeSite.ID);
});

 

So you can see the whole thing together:

   1: using NUnit.Framework;
   2: using TypeMock.ArrangeActAssert;
   3: using Microsoft.SharePoint;
   4: using TypeMock;
   5: using System;
   6:  
   7: namespace Test
   8: {
   9:     [TestFixture]
  10:     public class Test
  11:     {
  12:         [Test]
  13:         [Isolated]
  14:         public void TestUsingSPSecurity()
  15:         {
  16:             Mock spSecMock = MockManager.Mock(typeof(SPSecurity));
  17:             spSecMock.MockMethodCalled += new MockMethodCalledEventHandler(spSecMock_MockMethodCalled);
  18:             spSecMock.ExpectAlways("RunWithElevatedPrivileges");
  19:  
  20:             Guid siteGuid = Guid.NewGuid();
  21:             SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
  22:             Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
  23:             Isolate.WhenCalled(() => fakeSite.ID).WillReturn(siteGuid);
  24:  
  25:             SPSecurity.RunWithElevatedPrivileges(delegate()
  26:             {
  27:                 SPSite elevatedSite = new SPSite(siteGuid);
  28:                 Assert.AreEqual(elevatedSite.ID, fakeSite.ID);
  29:             });
  30:         }
  31:  
  32:         void spSecMock_MockMethodCalled(object sender, MockMethodCallEventArgs e)
  33:         {
  34:             if (e.CalledMethodName.Equals("RunWithElevatedPrivileges"))
  35:             {
  36:                 SPSecurity.CodeToRunElevated del = (SPSecurity.CodeToRunElevated)e.SentArguments[0];
  37:                 IAsyncResult res = del.BeginInvoke(null, null);
  38:                 res.AsyncWaitHandle.WaitOne();
  39:                 del.EndInvoke(res);
  40:             }
  41:         }
  42:     }
  43: }

 

Keep in mind, this is an example of how to get into delegates with TypeMock, so it could be extended into Impersonation and The Right Way™ of working with SharePoint security.

 

Happy Mocking!

Published Tuesday, February 10, 2009 4:05 PM by dterrell

Comments

No Comments
Anonymous comments are disabled