When developing SharePoint web parts, it is often useful to develop and test the component as an ASP.NET User Control first and then wrap then user control in a web part for deployment to SharePoint. This approach allows you to test the UI and functionality of your component in simplified environment without the added complexity of a SharePoint deployment. It helps to speed the development process by eliminating the need to do frequent deployments to SharePoint and by making it easier to debug in Visual Studio. However, to successfully utilize this approach, you have to overcome the following challenges:
1) Loading the user control into your web part wrapper
2) Stubbing out the SharePoint functionality and mocking out the SharePoint data used by your user control
3) Copying the .ascx files to a location in your test web project that mirrors their location in the SharePoint environment
There are several well known approaches and tools to help with the first two challenges. I will summarize those briefly and focus on an approach for meeting the third.
Loading the User Control
There are two approaches for loading the user control into your web part at run-time. The first approach is to develop a custom web part that calls the Page.LoadControl() method during page load to instantiate the user control and then calls Controls.Add() to add it to the web part’s Controls collection. The LoadControl() method takes the virtual path of the user control and returns a reference to the user control object. This object is then passed into Controls.Add() to add the control to the page for rendering.
Using this approach, the .ascx files are typically deployed to the {SharePointInstallationPath}12TEMPLATECONTROLTEMPLATES folder to make them available throughout your SharePoint installation. Use application specific subfolders to organize and namespace your user controls.
By default, SharePoint blocks web parts from accessing resources in the ControlTemplates folder. Do successfully load a user control at run-time, you will have to deploy your web part assembly to the GAC or configure code access security to allow the necessary access. Mike Becker gives a thorough explanation of this issue at his blog here (http://blogs.pointbridge.com/Blogs/becker_michael/Pages/Default.aspx).
The other approach for loading the user control into the web part is to use the SmartPart component. SmartPart is a web part that can host any ASP.NET user control. For more information, visit the SmartPart pages on CodePlex (http://www.codeplex.com/smartpart).
Isolating the User Control From SharePoint Functionality and Data
To leverage all of the advantages of the user control approach for development and testing, you have to completely isolate the user control from all SharePoint functionality and data. This can be done by keeping all SharePoint object model reference calls in the web part and passing any required data into the user control. By following this pattern, you can the stub out any necessary SharePoint functionality in your test web project and mock out the data that needs to be passed into the user control. To create your mock objects, you can use a mock object framework such as NMock (http://www.nmock.org/) or simply roll your own mocks. Whichever method you use to create your mocks, be careful that the mock data remains isolated to your test project. Mock data that makes it into your production codebase can cause havoc in your integration environment.
Copying the .ascx Files to Your Test Web Project
In your test web project, you should have a folder structure that mirrors the virtual path that SharePoint uses to expose files under the 12Template folder. For user controls, this would be _controltemplates{YourApplicationFolder}. If your .ascx files exist in this folder in your test project, they will be available in your test web at the same path that from which they will be available in SharePoint.
Keeping the .ascx files in your test project up-to-date can be an annoyance or even an outright burden if you are depending on .ascx files from multiple projects. However, the copy process can easily be automated by using MSBUILD. To set this up, unload your test project in Visual Studio and then open the .csproj file in the editor. Near the bottom of the file, you will find a commented out section that looks like the following:
<!– To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>–>
Uncomment the “AfterBuild” target and insert the following text:
<PropertyGroup>
<InputControlTemplatesFolder>CONTROLTEMPLATES</InputControlTemplatesFolder>
<InputLayoutsFolder>LAYOUTS</InputLayoutsFolder>
<OutputControlTemplatesFolder>_controltemplates</OutputControlTemplatesFolder>
<OutputLayoutsFolder>_layouts</OutputLayoutsFolder>
<SourceProjectRoot>….SourceRootFilesTEMPLATE</SourceProjectRoot>
<UserControlsRoot>……UserControlsSourceRootFilesTEMPLATE</UserControlsRoot>
</PropertyGroup>
<ItemGroup>
<SourceProjectControlTemplates
Include="$(SourceProjectRoot)$(InputControlTemplatesFolder)***.*" Exclude="$(SourceProjectRoot)$(InputControlTemplatesFolder)***.cs">
</SourceProjectControlTemplates>
<SourceProjectLayouts
Include="$(SourceProjectRoot)$(InputLayoutsFolder)***.*" Exclude="$(SourceProjectRoot)$(InputLayoutsFolder)***.cs">
</SourceProjectLayouts>
<UserControlsControlTemplates
Include="$(UserControlsRoot)$(InputControlTemplatesFolder)***.*" Exclude="$(UserControlsRoot)$(InputControlTemplatesFolder)***.cs">
</UserControlsControlTemplates>
<UserControlsLayouts
Include="$(UserControlsRoot)$(InputLayoutsFolder)***.*" Exclude="$(UserControlsRoot)$(InputLayoutsFolder)***.cs">
</UserControlsLayouts>
</ItemGroup>
<Message Text="Deleting folders…" Importance="high" />
<RemoveDir Directories="$(OutputControlTemplatesFolder)" />
<RemoveDir Directories="$(OutputLayoutsFolder)" />
<Message Text="Copying files …" Importance="high" />
<Copy SourceFiles="@(SourceProjectControlTemplates)"
DestinationFiles="@(SourceProjectControlTemplates->’$(OutputControlTemplatesFolder)%(RecursiveDir)%(Filename)%(Extension)’)" />
<Copy SourceFiles="@(SourceProjectLayouts)"
DestinationFiles="@(SourceProjectLayouts-
>’$(OutputLayoutsFolder)%(RecursiveDir)%(Filename)%(Extension)’)" />
<Copy SourceFiles="@(UserControlsControlTemplates)"
DestinationFiles="@(UserControlsControlTemplates->’$(OutputControlTemplatesFolder)%(RecursiveDir)%(Filename)%(Extension)’)" />
<Copy SourceFiles="@(UserControlsLayouts)"
DestinationFiles="@(UserControlsLayouts->’$(OutputLayoutsFolder)%(RecursiveDir)%(Filename)%(Extension)’)" />
Update the SourceProjectRoot and UserControlsRoot properties to point to the projects from which you want to copy .ascx files into your test web’s folder structure. These paths must be relative to folder that contains the test project file.
In the example above, the SourceProjectRoot points to a project in the same solution as the test project and the UserControlsRoot points to a project that contains common user controls. Additional projects can be added by following the pattern illustrated in the example.
These commands will copy the contents of your projects’ Control Templates and Layouts folders into the folder structure of the test web making them available to your test application from the same virtual paths at which they will be available in your SharePoint environment.