Unit Testing and Mocking IEnumerable/IEnumerator with NUnit and RhinoAutoMocker

Background

I have run into the unenviable task of maintaining someone else’s stale, and poorly architected codebase. One of my self-directed initiatives was to begin to implement some unit testing, especially in the most volatile areas of the application to begin to automate some of the sorely lacking regression testing as new features are added and defects are resolved. This is the last I’ll coment on the state of the current codebase and my incredulity that modern software applications are still build hand-to-keyboard first without thinking about design and architecture.

As I began to work with the existing code base, I ran into a situation where there were a handful of objects implement the old IEnumerable/IEnumerater (correct, not the generic kind). I began to feverishly attempt to unit test these but ran into all sorts of what I can only explain as “language semantic” errors. Granted it has been several years since I had to go back and look at the IEnumerable pattern to understand again how it works instead of just inheriting from List<>. For anyone else in my shoes, I’ll explain quickly and then get into the testing.

When the exercising code begins to enumerate over an IEnumerable, such as a foreach loop:


A couple different things happen. First, the loop calls “GetEnumerator()” from the IEnumerable object, in this case ProjectCollection, which returns an IEnumerator. IEnumerator implements a few different methods and properties, most notably:




MoveNext serves only to increment an indexer into the underlying array of objects with which the IEnumerator is working. If the incement is successful, i.e., it does not exceed the length of the underlying collection, we return true, otherwise false. As the exercising code iterates over the collection, it knows to break the loop once MoveNext returns false.

Current, then uses this indexer to return the current item in the underlying collection based on the value of the iterator. So a couple things we need to do in order to implement the pattern correctly. First, it is important to know the pattern assumes the initial value of the internal iterator is always less than the first value in the collection…usually -1.



Secondly, we should take care to do appropriate error handling since we are providing the iterating functionality ourselves…but with proper testing we will get there.

Start Reading Here

We will start working with the following series of classes:

Additionally, we have a worker class which we will use to exercise the enumeration with the following method:

ProductList GetProductDataList(string lanId)

And ProductList : List<Product>

OK, enough setup, let’s jump in. Testing the Enumerator and IEnumerable are pretty straight forward, and I won’t spend a lot of time there. You can look at the attached source code. The more difficult thing is to mock out and stub the behavior of these classes, especially since the ProductCollection uses a method called Load to hydrate itself from some data source such as a service or database.

The first step here is to dependency inject the ProjectCollection object into the Worker object…We can talk about IoC Containers and the like, but that is not the purpose of this blog so we will use a simple constructor injection pattern:

Now from our Test class we can use RhinoAutoMocker to setup the worker class and setup the Ctor DI of the IProjectCollection:

Next we will generate a mock IEnumerator to be returned by the GetEnumerator method of the IEnumerable object:

Now the fun starts, we need to start stubbing out the behavior of the interface:

Interestingly, here is where the problems start for me. I had originally stubbed out the MoveNext method to always return true without a limit. This causes the iterator in the exercising class to always return true and never break the loop. Which is why I need to put the .Repeat.Once() constraint on the call.

Similarly, the Current property will always return the same object if we don’t constrain it to only repeat once.

Once we have that all working, then we can set up the IProductCollection.GetEnumerator to return the productEnumerator we just mocked out:

And then call the underlying method on the exercising code and our assertions:

There you have it.

Source Code HERE

Uploading Documents to SharePoint (2007)…also works with 2010

I had an interesting request from a client today.  I am working on maintaining an existing project that has been in production for a few years now.  This is one of those situations where I have a limited budget and time and also I cannot fundamentally change the overall process.  Caveats aside, Nothing out of the ordinary, but interesting nonetheless. 

A little background…skip if you don’t care…

They have a simple approval workflow on a document library.  If approved, the document should be routed to an “Approved” document library, otherwise, it goes to a “Further Review” document library.  The routing happens due to item event receivers on the task list the workflow uses to push tasks to – remember the caveat above.  When a document is added to either library, a new workflow is kicked off.  This workflow adds another task to the task list to be evaluated by the event receiver. This time, the event receiver on the task list sends an email to original document submitter notifying them that they either need to review the document and make changes or it was successfully submitted.  Keep in mind this was working. 

Client stands up new farm and everything seems to be working, but no emails are being sent from the item event receiver.  Digging into it, turns out the new production farm is SP2 whereas the old (Legacy) farm was RTM.  As of MOSS 2007 SP1, workflows cannot be automatically initiated b y the “System Account” which caused the workflow on the “Review” or “Approve” document libraries never to run, since the item event receiver was using:

 

   1:  SPSecurity.RunWithElevatedPrivileges(delegate(){   
   2:      //... prepare document and do some other preprocessing          
   3:      // skipping for brevity   
   4:      web.Folders("Review").Files.Add(documentUrl, document
                                           , contents);   
   5:  });

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Smart consultant guy I am, I say, let’s get rid of the elevated privileges and use SharePoint permissions and impersonation to handle document upload.  Unfortunately, I am stricken with “Administrator Fever” where I am only used to working in an environment where I am the administrator.  We roll to Test and lots of smoke a little fizzle and small *pop*.  Getting access denied errors, COMPlusExceptions – 80004004 (E_ABORT) specifically — love those — and generally no bueno.   Digging through more archived blogs and cached google sites that I can recall, I remember needing to give WSS_WPG and WSS_WPG_ADMIN and physical database access to run code.  This will never work in a real production environment and I would have to hang my head in consulting shame if I went this direction.  Then I came across an interesting MSDN article regarding Vermeer content types – ahh I see you too have never heard of them.

The gist is that X-Vermeer-Content-Type is how we can make RPC calls to the SharePoint API without needing SharePoint.  wha? you ask…I’ll go deeper.  Just like specifying application/vnd.ms-excel as the MIME content type on a HTTP GET response that returns a CSV will automagically open Excel, you can do the same thing with SharePoint 2007 or 2010.  In fact, interacting with SharePoint 2010 in this way is not only supported, it is a recommended approach for ISVs and OEMs.  Microsoft has posted copious amounts of documentation about how this is done:

For my purposes, I will use the put+document method to solve my problem.

Here is a link to the Microsoft documentation about it:

I can dynamically build out my HTTP PUT with the correct content type and then I am able to upload documents to a SharePoint as the impersonated user and I can control security through the normal SharePoint interface.  Here is the end result:

   1:  public static bool Upload(string webUrl,    
   2:                              string documentName,    
   3:                              byte[] bytes,    
   4:                              Dictionary<string, object> metaInfo,   
   5:                              out string result)  
   6:    {   
   7:    string putOption = "overwrite,createdir,migrationsemantics";   
   8:    // see http://msdn2.microsoft.com/en-us/library/ms455325.aspx               
   9:    string comment = null;  
  10:    bool keepCheckedOut = false;  
  11:    string method =  
  12:        @"method=put+document%3a12.0.4518.1016&service_name=%2f&
  13:            document=[document_name={0};
  14:            meta_info=[{1}]]&
  15:            put_option={2}&
  16:            comment={3}&
  17:            keep_checked_out={4}\n";  
  18:    method = String.Format(method, documentName, EncodeMetaInfo(metaInfo), putOption,  
  19:    HttpUtility.UrlEncode(comment), keepCheckedOut.ToString().ToLower());  
  20:    var data = new List<byte>();  
  21:    data.AddRange(Encoding.UTF8.GetBytes(method));  
  22:    data.AddRange(bytes);  
  23:    try  
  24:        {  
  25:            using (var webClient = new WebClient())  
  26:            {  
  27:                webClient.Credentials = CredentialCache.DefaultCredentials;  
  28:                webClient.Headers.Add("Content-Type", 
                                           "application/x-vermeer-urlencoded");  

29: webClient.Headers.Add("X-Vermeer-Content-Type",

"application/x-vermeer-urlencoded");

  30:                result = Encoding.UTF8.GetString(webClient.UploadData(webUrl 
                   + "/_vti_bin/_vti_aut/author.dll", "POST", data.ToArray()));  
  31:                if (result.IndexOf("\n<p>message=successfully") < 0)   
  32:                    throw new Exception(result);  
  33:                }  
  34:            }  
  35:            catch (Exception ex)  
  36:            {  
  37:                result = ex.Message;  
  38:                return false;      
  39:            }  
  40:        return true;  
  41:    }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The key here is two-fold:

  1. Specifying the content type:
     webClient.Headers.Add("X-Vermeer-Content-Type", "application/x-vermeer-urlencoded");

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }

      2.  Building out the method correctly and appending the byte[] contents of the document to the end of the method:

 

  
string method =  @"method=put+document%3a12.0.4518.1016&service_name=%2f&
            document=[document_name={0};
            meta_info=[{1}]]&
            put_option={2}&
            comment={3}&
            keep_checked_out={4}\n";
...

data.AddRange(Encoding.UTF8.GetBytes(method));
data.AddRange(bytes);

Other than that, it’s a pretty straight forward HTTP PUT (webClient.UploadData).

Hope this helps you out.

Happy 2010

Happy 2010! Should be an exciting year for all things Microsoft with the Wave 14 of Office and VS2010. I am fotunate to be on a project where I will get to develop some claims based authentication/oauth, analytics algorithms and generally recreating lots of wheels. I think it’s important to do this from time to time to keep those skills sharp and understand the inner workings of some of those decisions. Anyway, this post will hopefully whet your appetite for some of those discussions in the near future as this project kicks off and really gets rolling.

WiX Troubles

Technorati Tags: ,,

I recently had to create an installer for a service I was creating for a client.  Normally, I like to post successes and how I got past the hurdles that go me to success, but this time, I am relegated to posting a problem.  I am not trying to do much in the way of complexity and unfortunately, the WiX 3.0 Documentation is lacking.

I am trying to create an MSMQ Queue that my service will use after deployment.  There is some decent documentation about what parameters are needed to accomplish this here.  The installation actually is pretty smooth.  I have the following Component:

<MSMQ:MessageQueue
  Id="PMO.Queue"
  Label="PMO"
  PathName=".\Private$\pmo"
  Transactional="no"
  PrivLevel="none">

  <MSMQ:MessageQueuePermission 
   
User="ServiceAccount"
    QueueGenericAll="yes"
    DeleteQueue="yes" 
    WriteMessage="yes"
    ReceiveMessage="yes"
    TakeQueueOwnership="yes"
    Id="Queue.Permissions.PMO"/>

</MSMQ:MessageQueue>

The problem comes when I try to uninstall the service.  Mind you that once I try to build the MSI from Visual Studio that I break compatibility with the old MSI that has been installed.  So, make sure to make backups of successfully installed MSIs otherwise you will be digging through the depths of <RootDrive>:\Windows\Installer\*.MSI trying to find the right one.

Looking at my MSI log, I see the following:

MSI (s) (98:1C) [11:19:07:524]: Invoking remote custom
   action. DLL: C:\WINDOWS\Installer\MSI54D.tmp, 
   Entrypoint:  MessageQueuingExecuteUninstall
   MessageQueuingExecuteUninstall:  Error 0×80070534:
   Failed to lookup account names
MessageQueuingExecuteUninstall:  Error 0×80070534:
   Failed to get SID for account name
MessageQueuingExecuteUninstall:  Error 0×80070534:
   Failed to remove message queue permission
MessageQueuingExecuteUninstall:  Error 0×80070534:
   Failed to remove message queue permissions

Something that struck me was the “User” attribute on the MessageQueuePermission element.  The documentation says this should be a string.  Based on my experience with configuring IIS settings with WiX, I assumed this should be a <User /> element referencing the ID of that <User/> account.  So, I have created:

<Util:User 
  Domain="[DOMAIN]"
  Name="[ACCOUNT]"
  Password="[PASSWORD]"
  Id="ServiceAccount">
</Util:User>

To no avail.  I am going to continue researching this problem to see what I can come up with…

Project Server Event Handling Debugging

There are a myriad of posts on-line, including one web cast, which demonstrates in detail how to debug project server event handlers. Unfortunately, most of these posts talk about the happiest of paths and do not talk about what happens when things go wrong. I recently ran into a problem which took me several days to finally troubleshoot and figure out.

Here is the general scenario:

  1. Have a class which inherits from ProjectServerEventHandler
  2. I have several configuration settings in a custom configuration section.
  3. I use PSI to interrogate project server to get additional information about projects.
  4. I am using VS2008

The symptoms are:

  1. I attach the debugger to the Project Server Eventing Service process – more on this in a sec.
  2. I raise the OnProjectPublished Event by publishing the project through Project Professional.
  3. The debugger never breaks on my breakpoint

At this point, I was convinced it’s the debugger not loading the symbols. I try everything to load the symbols, still no love. The ULS logs are also of no help here. The application event log has entries that all follow this pattern:

Standard Information:PSI Entry Point:

Project User: LITWAREINC\Administrator

Correlation Id: d0593d1b-663d-426a-9174-6e94d5a334cc

PWA Site URL: http://litwaredemo/pwa

SSP Name: SharedServices1

PSError: GeneralActionCanceledBecauseServerEventServiceProblem (22002)

Action cancelled because of a problem with the Project Server Event Service.

Event that was cancelled: ProjectPublished.

 

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 

I also created a simple event handler which merely logs to a file, and the file never changes, but the error above keeps happening. One thing I’ve noticed from the project server development I’ve done in the past is that when I attach to the debugger there are typically two instances of the eventing service. This got me going down the right path. I figured that I was not seeing a second instance because my plug-in was not being loaded. Turns out the issue is that the configuration file (Microsoft.Office.Project.Server.Eventing.exe.config) The event log error above always occurs when the project server event service cannot load a custom plug-in for some internal reason, like messing up the config file.

 

Additionally, because the plug-in is not loaded, breakpoints are not hit. Anyway, here lies a very wordy explanation to a very simple problem. I am convinced that Microsoft will figure out one day that clear error messages will benefit us all!

Project Server Web Services

Download Source Here

Between Queuing service issues, the Cube Building service hackery, heavy reliance on datasets for web services, etc. etc. I am pretty comfortable saying that Microsoft Office Project Server 2007 (MOPS) is one of the worst released products by Microsoft. That being said, I have had the distinct pleasure recently being on three MOPS customization projects.

Initially, when beginning my decent into MOPS customization hell, I googled Binged Blog A and Group B to understand how to reference and use the various Web Services within MOPS. What I had a hard time with, as with any web service was my ability to dependency inject the service reference into my classes for easier unit testing. With generics, I implemented a solution which I am relatively happy with. Here are the details:

  1. Reference Web Service
  2. Extract interface of service
  3. Create web service partial class and implement extracted interface
  4. Set up Factory to create instance of service

Reference the web service

We’ve all done this before. The one thing I found that works best is to reference the web service as a 2.0 ASMX web service, not through WCF. I do like WCF a lot, but I had too much trouble with additional configuration settings, and authentication problems and looming deadlines I reverted to ASMXs.

Extract Interface of Service

First thing is to show all files in the project, then navigate to the reference.cs class, open this guy up and extract your interface. I use Resharper to do this, but VS can do the same thing and you can also do this yourself by just creating the signatures of the methods and properties that count. For simple reads of Project data, I only really need to implement the properties:

  • Url
  • Credentials
  • UseDefaultCredentials
  • CookieContainer

And the Methods:

  • ReadProjectList
  • ReadProjectEntities
  • ReadProject

When this is done, I have an interface that looks like:

Create Web Service Partial Class

Next, move your interface into the same namespace (folder) as the web service to keep everything together. In that namespace, I will create a partial class of the web service class. Have this partial class implement your interface and remove the implementation from the generated web service proxy. You should have something that looks like:

The web service proxy should be unchanged allowing you to regenerate it without affecting your new interface or implementation of that interface. Aren’t partial classes great!!?! Well done here MSFT to change the proxy generation to generate the proxy as a partial class.

Set up Factory to Create Instance of Service

You’ve done all of the hard work to have your service implement an interface for mocking purposes, but I always find it nice to wrap my service into a factory. This gives me some flexibility to leverage caching, which is especially important when using Project Server web services since they can return HUGE amounts of data. There are a couple levels of complexity you can use here for your service, but I like to be aggressive since it seems I always use at least two web services.

First, let’s create a namespace (folder) for our “Service Providers”. We want to create an abstract base class which all of our service providers (factories) will inherit. The base class will also use some generics to constrain what type of service provider it is:

Now we can implement our GetService method:

Next we can implement this base class:

The use of this pattern makes my development a ton easier:

Inheriting Global Navigation

I was recently asked how to enable Global Navigation on all sites within a site collection for MOSS 2007. PowerShell seemed like the best option. Here’s how it works:

 

param( $rootUrl
=
http://litwaredemo&#8221; )

“Setting STSADM”

$stsadm
=
“C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\STSADM.EXE”

 

“Getting Sites XML”

[xml] $sites
= &$stsadm -o enumsites -url $rootUrl

 

“Loading Microsoft.SharePoint”

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint.Publishing”)

 

 

 

 

function
SetGlobalNav([Microsoft.SharePoint.SPSite] $Site)

{

    $site.AllWebs | %{

        

        [Microsoft.SharePoint.SPWeb] $web
=
$SPSite.OpenWeb($_.ServerRelativeUrl)

        if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($web)) {

            [Microsoft.SharePoint.Publishing.PublishingWeb] $pweb
= [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

            $pweb.Navigation.UseShared =
$true    

        }

        

        $web.Dispose()

    }

}

 

 

function
EnumSiteCollections()

{

    “Getting Site Collection”

    $sites.Sites.Site | %{

        [Microsoft.SharePoint.SPSite] $SPSite
=
New-Object
“Microsoft.SharePoint.SPSite”
-argumentList
$_.Url

 

        “Setting Global Navigation for $($_.Url)”

        SetGlobalNav
$SPSite

        $SPSite.Dispose()

    }

}

 

EnumSiteCollections

 

The function SetGlobalNav is the heavy lifting. This assumes that the sub webs are all publishing webs. If not you can always replace:


[Microsoft.SharePoint.SPWeb]
$web
=
$SPSite.OpenWeb($_.ServerRelativeUrl)

if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($web)) {

    [Microsoft.SharePoint.Publishing.PublishingWeb] $pweb
= [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

    $pweb.Navigation.UseShared =
$true    

}

 

With:

 

[Microsoft.SharePoint.SPWeb] $web
=
$SPSite.OpenWeb($_.ServerRelativeUrl)

$web.Navigation.UseShared
=
$true

$web.Update()