Using FakePoint

Having arrived at a satisfactory way of building a unit test for a web part, we must now turn our attention properly to the subject of this project, because unfortunately none of the tests described in the preceeding section will work. This is because the coding of the WebPart assumes that it exists in the context of a web page, which is not the case when the unit test runs. As a result the call to SPContext.Current will return null.

At this point you may wonder whether it is worth bothering to have a unit test at all. After all, one could simply test the web part in a SharePoint site in the development environment. For a very simple web part which we do not anticipate changing further, or a throwaway component, perhaps this is sufficient. But bear in mind that this means setting up a live SharePoint site to run against, which would require a considerable amount of the configuration prior to running the test, which would make this an integration test rather than a unit test. This is the second major challenge we face in properly unit testing a WebPart; we must somehow provide a fake version of the SharePoint object model that will be populated with test data.

One way of doing this, which has proved very successful, is to build a fake set of SharePoint classes around an XML document which is populated by loading a CAML representation of a test SharePoint site. This is the same XML structure that can be found in SharePoint site templates and in deployment of objects in solution packages. If a site already exists with the desired content the CAML can therefore be retrieved either by creating a site template or using SharePoint designer.

FakePoint is a set of fake SharePoint object model classes that have been developed for this purpose. They can be downloaded and used as a starting point, although it is likely that they will need to be extended in order to support anything other than basic interaction with the SharePoint object model.
In order to use FakePoint, the test project is set up slightly differently to the default Visual Studio test project. Normally, when the test project is created, Visual Studio includes a reference to the output binaries of the project containing the production code. These assemblies are already linked to the SharePoint DLL, so simply replacing the reference to the SharePoint.dll with the FakePoint.dll will not work.

When using fake objects in this way it is necessary to devise a way of using them to replace the real SharePoint object model. It is for this reason that we include our web part classes as a linked source item in our test project, rather than including a reference to the web part project. This enables us to omit the reference to the SharePoint DLL and replace it with a reference to our fake DLL in the test project. For this to work the fake object model must also use the Microsoft.SharePoint namespace. We must ensure that the test project contains a reference to the FakePoint.dll and not SharePoint.dll, in order to avoid a namespace conflict. Obviously, the production code source files will be added to the test project as links so as not to end up with duplicate copies of the code.

The structure of the resulting visual studio solution is shown in the diagram below. In the production code the web part will make calls through the SharePoint DLL to the SharePoint farm (whether on the production server or a local SharePoint instance used for integration testing). The test project will include a separate build of the production code against the fake SharePoint DLL, which in turn will obtain its content from an XML file.

TestSolutionDiagram.gif

It is possible to maintain a second test project that runs against the normal SharePoint DLL as an integration test. If the CAML file used by FakePoint is maintained alongside a test site running on SharePoint, the same test files can be used for both integration and unit testing. This allows a single test to give the benefit of a rapid development cycle in conjunction with the additional quality assurance provided by an integration test against the actual SharePoint object model.

Capturing the CAML for the Test Site

There are a number of ways of the achieving this, including creating a site template through the SharePoint user interface, and using SharePoint designer. The preferred way is to use SPExplorer to capture a test site and save it to a CAML/XML file.

Setting up the test project

We assume that you already have a Visual Studio solution with a project containing your web part or other SharePoint project. First you need to create the test project by right-clicking on the solution and selecting Add->New Project. Create a new test project, taking care that it creates the new folder within your project folder. Visual studio creates the new project and some additional files in the solution to support the MSTest testing framework. If you are using another testing framework such as NUnit, you will probably just want to create a windows class library project instead.

Now right-click on the test project and choose Add Existing Item, navigate to your source code in your production project and select your source files. On the Add Existing Item dialog box you need to use the drop down list next to the Add button to change this to Add As Link. This is quite important because otherwise you will make copies of your source files and they will get out of sync. Click on the Add As Link button to incorporate the source code into your test project.

You will need to add to your test project any references that are required to build the source code, with the important exception of the Microsoft.SharePoint.dll - instead you will be adding the FakePoint.dll. You can now check that the test project builds without errors.

Creating Tests

When visual studio sets up the test project it creates a file UnitTest1.cs. This file can be used as the starting point for a unit test, although you may want to remove some of the placeholder code and comments in this default test file. It is a good idea to create a file and class corresponding to each of your web parts or other components with names such as WebPart1_UnitTest.cs and so on. It also makes things easier if the namespace of your test project is the same as the namespace you have used in your production code. A “using Microsoft.SharePoint” directive should also be added to avoid needing to prefix the SharePoint classes with the namespace.

The first thing that needs to be done is to ensure that FakePoint is initialised by making a call to SPContext.Initialize. The parameter to this method is the partial name of the CAML file containing the definition of the test site. In the example below the file will be “test_site.manifest.xml”. Initialization can be in each test method or, if the site does not need to be reset for every test, in a test initialization method for the class as a whole (using the TestInitialize attribute).

As explained in a previous section the unit test class should be derived from the component under test so that its protected members can be called in the test class. Using FakePoint, the test class shown earlier now looks as follows:

namespace TestProject2
{
  [TestClass]
  public class WebPart1Test : WebPart1
  {    
    [TestMethod]
    public void RenderContents_Tasks()
    {
      SPContext.Initialize("test_site");
      ListName = "Tasks";
      StringWriter sw = new StringWriter();
      HtmlTextWriter writer = new HtmlTextWriter(sw);
      EnsureChildControls();
      RenderContents(writer);
      Assert.IsTrue(FakePoint.Test.Verify(sw.ToString()));
    }
  }
}

This should now build and run, but will report an error because the Assert will fail. Looking inside the solution directory there should be a file WebPartTestRenderContentsTasks.expected, which will be empty, and also a file WebPartTestRenderContentsTasks.actual which contains the resulting HTML rendered by the web part. This latter file should be inspected. If the fake site contains a SharePoint list named “Tasks”, and everything has worked correctly, it should be possible to see that the correct output is rendered; otherwise it will be necessary to modify the code until this is the case. Once the output satisfies the requirements, the .actual file can be renamed to replace the .expected file, and the test will now pass. When the test passes no .actual file is generated.

Using FakePoint with a Mocking Framework

FakePoint can be used in conjunction with a third party mocking framework such as TypeMock Isolator. As an example, supposing that the WebPart is dependent on the current date and time. In this case we would use FakePoint to provide fake objects for the SharePoint object model, but that still leaves us with the problem of how to fake the DateTime class. Clearly we do not want to embark on building a fake version of the entire .net framework.
Using a tool of like TypeMock Isolator we would add the following code before calling the RenderContents method of the WebPart:

Isolate.WhenCalled(()=>DateTime.Now).WillReturn(new DateTime(2010,1,1));

TypeMock Isolator can also be used to add ad hoc initialisations for individual tests, while using FakePoint to deliver the bulk of the content from the fake SharePoint classes.

Last edited Feb 11, 2010 at 12:40 PM by flosim, version 9

Comments

No comments yet.