Wednesday, September 23, 2009

Understanding SharePoint’s “ddwrt:DataBind” syntax

SharePoint takes a lot of criticism from developers but it is really impressive what you can do without writing backend code. In my last large SharePoint implementation, I was customizing some input fields for a form using SharePoint Designer.  I found myself looking at the XSLT for a DataFormWebPart wondering how to decipher the auto-generated _designer:bind attribute.

<SharePoint:FormField runat="server" id="ff1{$Pos}" 
    ControlMode="Edit" FieldName="Field1"
    __designer:bind="{
        ddwrt:DataBind(
            'u', 
            concat('ff1',$Pos), 'Value', 'ValueChanged'
            'ID', ddwrt:EscapeDelims(@ID), '@Field1'
        )}"
    />
Strangely, the method ddwrt:DataBind is absent from what is probably the best online overview of the ddwrt XSLT extension object.  So I threw the question onto Stack Overflow to see if any bright people could shed light on the mysterious method.  Not surprisingly, no one could answer the question.  So I decided to do some spelunking in Reflector to see what I could find.
I opened reflector, added a reference to Microsoft.SharePoint then opened the search window.  Using the “Member Search” (tip: CTRL+M) I searched for "DataBind" and found the internal class, Microsoft.SharePoint.WebPartPages.DataFormDdwRuntime.  Some parts of this assembly are obfuscated, but by guestimation this is likely the extension object that is available to us in the XSLT under the namespace "ddwrt".
Oddly enough, the DataFormDdwRuntime.DataBind method returns an empty string, so it’s likely that the “__designer:bind” attribute is just a dummy attribute for SharePoint Designer, if at all.  Remember that curly braces inside an attribute is an XSLT expression, so the computed result is actually __designer:bind=””.  The real magic is that what the DataBind method actually does.

Behind the DataBind Method

Turns out that the DataBind method is basically a wrapper to the DataFormWebPart method, AddDataBinding.  This method has the following signature:
public override void AddDataBinding(string op,
                                    string controlId,
                                    string propertyName,
                                    string eventName,
                                    string keyField,
                                    string keyValue,
                                    string dataField
                                    ) 
As the name implies, it creates a data binding and associates it with the hosted WebPart, but the msdn documentation leaves us guessing as to what values should be used.  Reflector provides us some guidance.
Note:  keyField, keyValue and dataField all refer to methods that are obfuscated.  The detail below for these fields is my best guess, so take it AS-IS with no warranty.  As always, your comments, suggestions and feedback are welcome.
Operation Type (“op”)
This is the type of operation that will be used when the form is submitted. There are three possible values:  "i” (Insert), “u” (Update") and “d” (Delete).  The logic falls back to “update” if it doesn’t recognize the value passed in.
Bound Control (“controlId”)
This the id of the control that will be will be bound to the SharePoint list item.  The auto-generated value attempts to duplicate the value of the “id” attribute using an xslt function concat.  You can use your own format, but the chosen format for the id’s are “ff + field index + record index” where the field index is an arbitrary number, usually the order in which the fields appear in the designer (1,2,3, etc), and the keyword $Pos is an XSLT variable defined earlier in scope, referring to the position() of the row in the result set.
Bound Control Property (“propertyName”)
This is the property on our bound control that will ultimately be used to represent the SharePoint field value.  For SharePoint FormField controls, this property is “Value”.  Most ASP.NET controls can be used, assuming that the propertyName and eventName are properly set.
Bound Control Event (“eventName”)
This the event on the bound control that you want to be handled when the form posts information back to the server.  How this works is really interesting: it uses reflection to get the Event of your control, then binds the postback event to the DataFormWebPart's HandleChangedField method.  This method essentially fetches the value of your control (via the PropertyName) and then tracks that the field has changed or needs to be inserted.
For SharePoint FormField controls, this event is “ValueChanged”.
Data Source Primary Key (“keyField”)
Based on examples, the “key field” refers to the attribute in the dsQueryResponse XML which represents the primary key for this record.  This is likely always going to be "ID".  (ie, /dsQueryResponse/Rows/Row/@ID)
Data Source Primary Key Value (“keyValue”)
The value of the primary key, “ID”.  The ddwrt:EscapeDelims method is used to convert certain characters (,=;'{}") to a SharePoint unicode string equivalent (ie, “,” is converted to “_x002C_”).  For SharePoint Designer XSLT, this is likely always going to be ddwrt:EscapeDelims(@ID).
Data Source Field Name (“dataField”)
This is the name of the SharePoint field that we want to bind.  Since we’re pulling this value from the dsQueryResponse XML, it’s represented as it appears in the dsQueryResponse XML.

Some Examples

Updating a SharePoint text field using an ASP.NET TextBox:
<asp:TextBox 
    runat="server" 
    id="myTextBox{$Pos}"
    Text="{@Field1}" 
    Value="{@Field1}"
    __designer:bind="{ddwrt:DataBind(
                'u', 
                concat('myTextBox', $Pos),
                'Text',
                'TextChanged',
                'ID', 
                ddwrt:EscapeDelims(@ID),
                '@Field1')}" />
Insert a SharePoint text field as a ASP.NET DropDownList:
<asp:DropDownList 
    runat="server"
    id="myDropDown{$Pos}"
    __designer:bind="{ddwrt:DataBind(
        'i',
        concat('myDropDown', $Pos),
        'SelectedValue', 
        'TextChanged',
        'ID',
        ddwrt:EscapeDelims(@ID),
        '@Field2'}" />

Conclusion

If you’re using the auto-generated XSLT, the default format works fine.  However, if you want to craft the markup by hand or roll your own customizations, understanding this syntax is helpful.

submit to reddit

Monday, September 21, 2009

About the Selenium Toolkit for .NET

As part of the development process my open source project, I’ll be using my blog to post updates.  So subscribe via RSS, by email, follow me on twitter, or just bookmark me.

The Selenium Toolkit for .NET is based on my experiences implementing Selenium for .NET based projects.  When I started this project, I set out with the goal to minimize the time investment needed for developers to come on board, so they could concentrate on developing and running tests sooner.  This has largely been realized by separating the test-framework plumbing from the tests using an nunit addin and streamlining the setup process by creating an installer package that contains several utilities in a single download.

One of the more interesting and useful features that has come out of that initial goal is the ability to configure the tests through configuration elements in the app.config.  This feature makes it easy to run slightly different configurations between developer machines and build server without having to alter the tests.  Beyond this feature, and what excites me most, is that the toolkit and its configuration are separate components from the nunit addin.  This division makes it possible to include addins for other testing frameworks such as MSTest, xUnit, mbUnit, etc when available.  In addition, the toolkit uses a provider model for creating selenium instances.  This extensibility model will hopefully lead to future versions of the toolkit with features such as proxy-server detection, embedded user-extensions and alternate user-defined implementations of the ISelenium interface.

On the NUnit addin side, I'm presently compiling against version 2.4.8 simply because it was the framework I started the project with.  Unfortunately, the current design of the addin has a dependency to a specific nunit assembly, which results in some runtime errors if a newer version of nunit is used.  I'll likely be moving to 2.5.2 within a week if I find the time to do some regression testing.  Down the road I might investigate changing the addin to take advantage of some of NUnit's newer features, such as the RowTest extension.

The installer is functional, but there's still work to be done.  For instance, I'd like to have the Selenium IDE silently install into Firefox as part of the overall installation, and I'd like to provide some examples and Visual Studio Templates included as well.  I'll likely be converting from the Visual Studio installer package to a WiX format, to provide greater flexibilty during the user-experience.  WiX projects can also be built from the command-line (whereas VS installer projects can't), which will help to automate the packaging process.

Currently, the toolkit is listed as "beta" only because I can't guarantee that some of the interfaces won't change.  If you're downloading and running for tests, you might have to update some assemblies or tweak app.config files, but the core logic for running the tests shouldn't change.

The toolkit is available here:  http://seleniumtoolkit.codeplex.com

Wednesday, September 16, 2009

Testing NUnit Addins from within NUnit

Injecting your code into someone else's container

Note: This post refers to an internal feature of the NUnit.Core library and cannot be guaranteed in future versions.

Yesterday I released my first open source project, Selenium Toolkit for .NET, if you’re into functional web-testing I highly recommend you check it out.  I’d like to share a piece of code from that project – it’s either the biggest hack I’ve ever done or pretty clever.  Let me know what you think.

I don't like manually doing things more than once if I have to. Ironically, while building a tool to help automate functional web testing, I found that I had to do a lot of manual verification despite all the unit tests that I had written for my components. It started to get pretty frustrating: make a small change to my addin, uninstall and reinstall it, then open and run my sample test-fixtures to see how the NUnit framework had interpreted my changes. If I was going anywhere with this project, this was going to slow me down.

Fortunately, while spelunking through the NUnit.Core library, I found a really useful utility class that the fit the bill nicely and worked out pretty well. The TestAssemblyBuilder uses the nunit internals to find all the tests in an assembly and put them into a single Suite for inspection, execution, etc.

namespace Example
{
    using NUnit.Core.Builders;   

    public class ExampleTest
    {
        [Test]
        public void CanGetTestCount()
        {
            TestAssemblyBuilder assemblyBuilder = new TestAssemblyBuilder();
            TestSuite suite = assemblyBuilder.Build("AssemblyName.dll", false);

            Assert.AreEqual(4, suite.TestCount, "Not all tests were found.");
        }
    }
}

What’s really interesting here is the TestAssemblyBuilder uses a singleton within the NUnit internals, CoreExtensions.Host, to resolve all the core components for building up the suite.  This object implements the IExtensionHost interface which is also used to install our addin:

public interface NUnit.Core.Extensibility.IAddin
{
    bool Install(IExtensionHost host);
}

With this singleton at our disposal this is a pretty powerful option as I can now inject my addin into the framework with any configuration or mock that I want and test without environment dependencies.  In this example, I’m able to test how many times my Selenium instance would be started without ever launching a java host or browser:

namespace SeleniumToolkit.NUnit.Tests
{
    using NUnit.Core.Builders;
    using Selenium;
    using SeleniumToolkit;
    
    [TestFixture]
    public class FixtureBuilderTests
    {
        [Test]
        public void When_Fixture_Owns_Session_SeleniumFactory_Should_Only_Create_Sessions_For_Fixtures()
        {
            // create config that influences how the addin will work inside NUnit
            SeleniumConfiguration config = new SeleniumConfiguration();
            config.AddinSettings.RecyclePerFixture = true;
            
            // create basic event listener and suite-builder for installation into addin
            var eventListener = new SeleniumEventListener(null); // null = no java host
            var suiteBuilder = new SeleniumTestFixtureBuilder(config);

            // mock out the factory that creates our selenium objects
            var factory = MockRepository.GenerateMock<ISeleniumFactoryProvider>();
            var selenium = MockRepository.GenerateMock<ISelenium>();

            // tell our mock'd factory to always return our mock selenium
            factory.Expect(f => f.Create(null,0,null,null)).IgnoreArguments()
                   .Return(selenium).Repeat.Any();

            // set our expectations for how many times selenium will start/stop
            selenium.Expect(s => s.Start()).Repeat.Times(3);
            selenium.Expect(s => s.Stop()).Repeat.Times(3);

            // inject our mock into the toolkit
            SeleniumFactory.Configure(config, factory);

            // install addin
            SeleniumNUnitAddin addin = new SeleniumNUnitAddin();
            addin.Install(CoreExtensions.Host, suiteBuilder, eventListener); // injection method

            // using installed addin, test construction of fixtures
            var assemblyBuilder = new TestAssemblyBuilder();
            TestSuite suite = assemblyBuilder.Build( 
                                               "NUnit.TestAssembly.FixtureScenario.dll", 
                                               false);                        

            // run our sample fixtures against our configured runtime
            suite.Run(eventListener);

            // verify our behavior is as expected
            selenium.VerifyAllExpectations();
        }
    }

}

I should point out here that I don’t have my addin installed in NUnit’s addin folder. In fact, because I’m manually injecting it into NUnit, installation isn’t necessary. I also discovered that having the addin installed introduced some unexpected behavior with the tests in a few places.

To ensure that I'm testing my addin in isolation, it's important to clean up the CoreExtensions singleton after each test. While the NUnit core provides a mechanism to remove items from its extension points, it requires that you pass in the instances you want to remove, which didn’t quite fit my needs.  I suppose I could have implemented equality comparisons in my extensions, but I opted for the hack ‘em out using reflection option. It’s gross, but it works:

public class AddinUtil
{
    public static void Uninstall()
    {
        IExtensionPoint eventListeners = CoreExtensions.Host.GetExtensionPoint("EventListeners");
        IExtensionPoint suiteBuilders = CoreExtensions.Host.GetExtensionPoint("SuiteBuilders");
        IExtensionPoint testBuilders = CoreExtensions.Host.GetExtensionPoint("TestCaseBuilders");

        RemoveExtension(typeof(SeleniumEventListener), eventListeners);
        RemoveExtension(typeof(SeleniumTestFixtureBuilder), suiteBuilders);
        RemoveExtension(typeof(SeleniumTestCaseBuilder), testBuilders);
    }

    private static void RemoveExtension(Type type, IExtensionPoint extensionPoint)
    {
        ArrayList items = GetInternalExtensions(extensionPoint);
        ArrayList toRemove = new ArrayList();

        foreach (object item in items)
        {
            if (type.IsAssignableFrom(item.GetType()))
            {
                    toRemove.Add(item);
            }
        }

        foreach (object item in toRemove)
        {
            items.Remove(item);
        }
    }

    private static ArrayList GetInternalExtensions(IExtensionPoint extensionPoint)
    {
        object extensions = extensionPoint.GetType()
					  .GetField("extensions", 
							BindingFlags.NonPublic | 
							BindingFlags.Public | 
							BindingFlags.Instance)
					  .GetValue(extensionPoint);
        return (ArrayList)extensions;
    }
}

Conclusion

This technique provides a brief glimpse of how to use automate testing nunit addins inside nunit using dependency injection.  I’m fairly certain that I will still have to manually verify the addin’s behaviour from time to time, but with this approach I hopefully won’t have to do it that often.

submit to reddit

Announcing Selenium Toolkit for .NET

I’m pleased to announce that I’ve released my first open source project!

The Selenium Toolkit for .NET contains a bundled installer for Selenium libraries (Selenium-RC, Selenium IDE and Selenium client library for .NET) and an NUnit Addin that makes getting started with Selenium easy.

More information can be found here:  http://seleniumtoolkit.codeplex.com/