But it works on my PC!

The random thoughts of Richard Fennell on technology and software development

Mocking Sharepoint for Testing

In my previous post I talked about using Isolator to mock Sharepoint to aid the speed of the development process. I find this a productive way of working, but it does not really help in the realm of automated testing. You need a way to programmatically explore a webpart, preferably outside of SharePoint to check its correctness.

You could use the methods in my previous post and some form of automated web test, but this does mean you need to spin up a web server of some descriptions (IIS, Cassini etc. and deploy to it) An alternative is look at the Typmock addin Ivonna. This a creates a fake web server to load you page and tools to explore it.

I will describe how to use this technique using the same example as my previous post.

Previously I had placed all the code to fake out SharePoint in the Page_Load event of the test harness page. As I am now trying to write an unit/integration test I think it better to move this into the test itself so I would delete code I placed in the Page_Load event other than any property settings on the actual webpart. I would actually refactor the lines creating the fake URL context and fake SPSite into some helper methods and then call them from my new test. I would then load the page in Ivonna and check it’s values.

I have tried to show this below, using a couple of techniques to show how to get to components in the page.

   1: [TestMethod, Isolated]
   2:      public void LoadWebPage_SpSimpleWebPart_3EntriesInList()
   3:      {
   4:          // Arrange
   5:          TestHelpers.CreateFakeSPSite();
   6:          TestHelpers.CreateFakeURL();
   7:  
   8:          TestSession session = new TestSession(); //Start each test with this
   9:          WebRequest request = new WebRequest("/SpSimpleTest.aspx"); //Create a WebRequest object
  10:          
  11:          // Act
  12:          WebResponse response = session.ProcessRequest(request); //Process the request
  13:  
  14:          // Assert
  15:          //Check the page loaded
  16:          Assert.IsNotNull(response.Page);
  17:          
  18:          // the the Ivonna extension method to find the control
  19:          var wp = response.Page.FindRecursive<DemoWebParts.SpSimpleWebPart>("wp1");
  20:          Assert.IsNotNull(wp);
  21:  
  22:          // so we have to use the following structure and dig knowing the format
  23:          // webpart/table/row/cell/control
  24:          var label = ((TableRow)wp.Controls[0].Controls[0]).Cells[1].Controls[0] as Label;
  25:          Assert.IsNotNull(label);
  26:          Assert.AreEqual("http://mockedsite.com",label.Text);
  27:  
  28:          var list = ((TableRow)wp.Controls[0].Controls[1]).Cells[1].Controls[0] as DropDownList;
  29:          Assert.IsNotNull(list);
  30:          Assert.AreEqual(3, list.Items.Count);
  31:      }

Now I have to say I had high hopes for this technique, but it has not been as useful as I would hope. I suspect that this is due to the rapid changing of the UI design of client’s webparts making these tests too brittle. We have found the ‘mark 1 eyeball’ more appropriate in many cases, as is so often true for UI testing.

However, I do see this as being a great option for smoke testing in long running projects with fairly stable designs.

Mocking Sharepoint for Design with Typemock Isolator

I have found the most productive use of Typemock Isolator with SharePoint is to use it to reduce the time of the F5 cycle (build/deploy/use). If you are using a VPC of some type to do your SharePoint development, as many do, this process can easily take a couple minutes, and these minutes add up.

In my experience webparts usually make fairly simple use of the underlying SharePoint site, by this I mean that they get some data from an SPList(s) or remote data source and render in some way. Or the reverse, they gather data that they drop to an SPLits(s) or remote data source.

So why not remove the requirement for SharePoint during development from the equation? Mock it out with Isolator and an ASP.NET test site.

image

Consider this scenario…

  • You have a part that lists people name and email address in a combo box
  • This data is stored in an SPList
  • The webpart must also list the URL of the site it is hosted on.

All fairly straight forward, but how to implement the wrapper around it?

  • The first we create an ASP.NET Web Application and an ASP.NET web page in the same solution as the WebParts class library.
  • In this web application reference the webpart project
  • Edit the .ASPX to reference the webpart assembly (line 2) and create an instance on the page (line 11-17). It is best to do this declaratively to avoid any question of the ASP.NET personalisation system.

 

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SpSimpleTest.aspx.cs" Inherits="TestWebSite.SpSimpleTest" %>
   2: <%@ Register Assembly="DemoWebParts" Namespace="DemoWebParts" TagPrefix="wp" %>
   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   4: <html xmlns="http://www.w3.org/1999/xhtml">
   5: <head runat="server">
   6:     <title>Untitled Page</title>
   7: </head>
   8: <body>
   9:     <form id="form1" runat="server">
  10:     <div>
  11:         <asp:WebPartManager ID="WebPartManager1" runat="server">
  12:         </asp:WebPartManager>
  13:         <asp:WebPartZone ID="WebPartZone1" runat="server" >
  14:             <ZoneTemplate>
  15:                 <wp:SpSimpleWebPart ID="wp1" runat="server" />
  16:             </ZoneTemplate>
  17:         </asp:WebPartZone>
  18:     </div>
  19:     </form>
  20: </body>
  21: </html>

  • If you browse to this page you will see a null object exception as it loads the web part but it fails when it calls to SharePoint. This is because we have not mocked that yet. Seeing this error does rely on the fact you have a nice big global Try/Catch in the webparts Render() and CreateChildControls() methods. Note a technique I normally recommend but I think vital for this type of component else errors get swallowed by SharePoint

image

  • Add a reference to the SharePoint assemblies and Typemock Isolator (which of course you need a licensed copy of, or the 30 day demo) in the Test web application
  • In the Page_Load event you now need to add the code to do the faking, I think the comments below explain what is going on. (There is a good argument to refactor much of this into a TestHelper class so it is easy to reuse).
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using System.Web.UI;
   6: using System.Web.UI.WebControls;
   7: using TypeMock.ArrangeActAssert;
   8: using Microsoft.SharePoint;
   9:  
  10: namespace TestWebSite
  11: {
  12:     public partial class SpSimpleTestWithMockedSP : System.Web.UI.Page
  13:     {
  14:         protected void Page_Load(object sender, EventArgs e)
  15:         {
  16:  
  17:             // set the name of the list to read data from
  18:             wp1.DataList = "ListName";
  19:  
  20:             // set the fake return value for the currently running context
  21:             // we can us null as the current parameter as this is what this web page will return
  22:             Isolate.WhenCalled(() => Microsoft.SharePoint.WebControls.SPControl.GetContextSite(null).Url).WillReturn("http://mockedsite.com");
  23:  
  24:  
  25:             // create the mock SP Site we are using
  26:             SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
  27:             Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
  28:  
  29:             // create a fke collection to hold our test data
  30:             var itemCollection = new List<SPListItem>();
  31:             for (int i = 0; i < 3; i++)
  32:             {
  33:                 var fakeItem = Isolate.Fake.Instance<SPListItem>();
  34:                 itemCollection.Add(fakeItem);
  35:  
  36:                 Isolate.WhenCalled(() => fakeItem["Title"]).WillReturn(string.Format("Title {0}", i));
  37:                 Isolate.WhenCalled(() => fakeItem["Email Address"]).WillReturn(string.Format("email{0}@email.com", i));
  38:  
  39:  
  40:             }
  41:  
  42:             // set what is returned when a call is made for the list
  43:             Isolate.WhenCalled(() => fakeSite.RootWeb.Lists["ListName"].Items).WillReturnCollectionValuesOf(itemCollection);
  44:         }
  45:     }
  46: }
  • Once this code is entered you can browse to the page again and you should see a working webpart that thinks it is talking to a real SharePoint site.

image

So now you have a way to run you SharePoint dependant webpart outside of SharePoint.This means the F5 cycle is reduced to seconds as opposed to minutes, and debugging is loads easier.

What I find this particularly useful for is sorting CSS and JavaScript issues, where is loads of tiny edits to text files.

THIS DOES NOT MEAN YOU DON’T NEED TO TEST IN SHAREPOINT, but it does means you can avoid much of it. The SharePoint phase become far more a test/QA/UAT process as opposed a development one. This model as a developer productivity enabling one.

In my next post I will talk about Mocking Sharepoint for Test with Typemock Isolator

Speaking at Developer Day South West

I have just heard I will be speaking at Developer Day South West on June the 5th. My subject is Using the new Developer and Test features of VS 2010 to track down and fix bugs, this is basically the same session as I have at our TechDays fringe event yesterday.

Hope to see you there

DDDSouthWest2BadgeSmall[1]

Error running Ivonna tests with ASP.NET 4

When I tried to run a working Ivonna test, previously targeted at .NET 3.5, against .NET 4 I found my test failing with the error

------ Test started: Assembly: Webpart.Tests.dll ------
<html><body>Bad Request</body></html>
Setup information
Physical Web path: C:\Projects\Test\TypeMockSample\TestWebSite
Actual path: C:\Users\fez\AppData\Local\Temp\Temporary ASP.NET Files\root\156567f2

Turns out that the fix to simple, you have to use an absolute path i.e. the / in front of the BasicTest.aspx is vital

 

[TestMethod, Isolated]
        public void LoadWebPage_HelloWorldWebPart_NoError()
        {
            TestSession session = new TestSession(); //Start each test with this
            WebRequest request = new WebRequest("/BasicTest.aspx"); //Create a WebRequest object
            WebResponse response = session.ProcessRequest(request); //Process the request
            System.Web.UI.Page page = response.Page;
            //Check the page loaded
            Assert.IsNotNull(page);
        }

So this is a temporary work around for now, for me it is not an issue having to use a absolute path. I understand from the writer of Ivonna that this issue will be further investigated now that .NET 4.0 has RTM’d

Running Fitnesse.NET tests using MSTest - Revisited

In 2008 I wrote a post Running Fitnesse.NET tests using MSTest.  Recently I had need to us this technique on a VS2010 project, and as is so often the issue the code that worked then does not seem to work now. All I can assume is that the Fitnesse API had altered, but I thought I was using the same assemblies!

So I pulled down the code from http://sourceforge.net/projects/fitnessedotnet/ and had a poke about. Basically I seemed to be using the command line switches wrong. The listing below shows what I found is the working usage:

[TestMethod]
    public void WorkFlowSwitchOnName_DataViaFitnesse_Success()
    {
        fit.Runner.FolderRunner runner = new fit.Runner.FolderRunner(new fit.Runner.ConsoleReporter());
        var errorCount = runner.Run(new string[] {
                "-i",@"WF Workflow", // the directory that holds the HTM test files
                "-a",@"TestProject.dll",  //we have the fit fascard in this assembly
                "-o",@"results"}); // the directory the results are dumped into as HTML
        // fit can fail silently giving no failures as no test are run, so check for exceptions
        Assert.AreEqual(false, Regex.IsMatch(runner.Results, "^0.+?0.+?0.+?0.+?$"), "No tests appear to have been run");
        // look for expected errors
        Assert.AreEqual(0, errorCount, runner.Results);
        
    }

The –i option is the one that was wrong. I had been specifying a single HTM file. What I should have done was specify a directory that contains one or more HTM files. I handled this as a sub directory in my project with its contents set to copy to the output directory.

Once I made these edits I had my tests running again as expect.

Mixed mode assembly is built against version 'v2.0.50727' error using .NET 4 Development Web Server

If your application has a dependency on an assembly built in .NET 2 you will see the error below if you try to run your application when it has been built in.NET 4.

Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.

This can be important in VS2010 testing, as test projects must be built as .NET 4, there is no option to build with an older runtime. I suffered this problem when trying to do some development where I hosted a webpart that make calls into SharePoint (that was faked out with Typemock Isolator) inside a ASP.NET 4.0 test harness

The answer to this problem is well documented, you need to add the useLegacyV2RuntimeActivationPolicy attribute to a .CONFIG file, but which one? It is not the web.config file you might suspect, but the C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.exe.config file. The revised config file should read as follows (new bits in red)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
            <supportedRuntime version="v4.0" />   
  </startup>

  <runtime>
    <generatePublisherEvidence enabled="false" />
  </runtime>
</configuration>

Note: Don’t add the following <supportedRuntime version="v2.0.50727" />  this cause the web server to crash on start-up.

Post QCon thoughts

Interesting time at QCon yesterday,  shame I was only there one day, I do like the events that are not limited to a single vendor or technology. The multi presenter session I was involved in on Microsoft interoperability seemed to go well, there is talk of repeating at other events or podcasting. It is a nice format if you can get the sub-sessions linking nicely, like themed grok talks. 

Due to chatting to people (but that why you go really isn't it?), I only managed to get to one other session, but I was the one I wanted to see, Roy Osherove’s on using CThru to enable testing of monolithic frameworks such as Silverlight. It got a few things clearer in my mind over using CThu, a tool I have tried to use in the past but not had as much success as I hoped. So I think I will have another go at trying to build a SharePoint workflow testing framework, the problem has rested on the back burner too long. I think I just need to persist longer in digging to the eventing model to see why my workflows under test do not start. Roy’s comment that there is no short cut for this type of problem to avoid an archaeological excavation into the framework under test, I think is the key here.

Do you use a Symbol Server?

I find one of the most often overlooked new features of 2010 is the Symbol Server. This is a file share where the .PDB symbol files are stored for any given build (generated by a build server, see Jim Lamb’s post on the setup). If you look on the symbol server share you will see directories for each built assembly with a GUID named subdirectory containing the PDB files for each unique build.

So what is this Symbol Server used for? Well you can use the Symbol Server to enable debug features such as Intellitrace, vital if you are using Lab Manager. In effect this means that when viewing an Intellitrace log Visual Studio is able to go to the Symbol Server to get the correct .PDB file for the assemblies being run, even if the source is not available, thus allowing you to step through the code. It can also be used for remote debugging of ASP.NET servers.

A bonus is that you can debug release code, as long as you produced .PDB symbols and placed them on the Symbol Server when you built the release (by altering the advanced build properties shown below).

image

Key to remember here is that the client PC that generates the Intellitrace file does not need access to the PDB files, only the PC handling the debugging process needs to be able to access the symbols. Perfect for release codes scenarios.

This ability to debug into code that you don’t have the source for extends to debugging into Microsoft .NET framework code. Microsoft have made public a Symbol Server for just this purpose. To use it you have to enable it using the Tool > Option > Debugging > Symbols dialog.

image

All this should make debugging that hard to track problem just that bit easier.