vrijdag 25 juni 2010

Themeparks, jogging, and life


In the Efteling (theme park) they have a rollercoaster ride called "The Flying Dutchmen". The queue, which starts outside of the building, winds it way through a maze of small rooms inside until finally reaching the ride.

And this is annoying! Much more annoying than if the entire long queue were just visible at one glance. The reason is that each time you enter a new room, you hope that this is the last one. Then when it isn't you're dissapointed. But then of course you hope that this is really the last one... etc.

In contrast, when jogging you want a nice path winding through the woods with lots of twists and turns. Nothing is more discouraging than seeing an endless road stretch on before you into the distance, knowing that you'll have to run all that way. In the forest, you may have no idea how far you still need to go, but you can at least accomplish a smaller goal of reaching the next bend.

The difference between jogging and waiting in a queue? For the queue your goal is the ride, the queue is just an unfortunate necessity. For jogging, the goal is the jogging itself, where you end up isn't really all that important.

Ideally, life is like jogging in the forest, but all too often it feels like waiting in the queue.

vrijdag 4 juni 2010

SharePoint and automated testing


SharePoint is a horrible platform to build applications on. And even more so to automate testing against, with the ui and the data layer so completely interwoven. Even more difficult is writing tests for code spaghetti already written on top of SharePoint by somebody else. Nevertheless, this is what I've been doing the last couple of weeks and actually having some fun with it too. Thought I might share some of the things I learnt.

As refactoring the existing spaghetti code was not an option, I ended up building automated acceptance and integration tests that had to deal explicitly with the SharePoint environment. There were few opportunities for unit tests but if I have time I'll definitely put them in place too.

TIP 1

First off, most existing spaghetti code on top of SharePoint will use the global SPContext.Current somewhere. Nasty, nasty, nasty... . Of course, this dependency should have been nicely injected into the object using it, but this is seldom the case. So to create a "fake" SPContext for testing purposes, use code along the lines of (see also here):


String url = "http://myhost.com/mysitecollection";
SPUserToken token;
using (SPSite site = new SPSite(url))
{
token = site.SystemAccount.UserToken;
}
SPSite site = new SPSite(url, token);
HttpRequest request = new HttpRequest("", url, "");
HttpContext.Current = new HttpContext(request, new HttpResponse(TextWriter.Null));
HttpContext.Current.Items["HttpHandlerSPWeb"] = site.OpenWeb();


Explanation: SPContext looks in the current HttpContext for a reference to the current SharePoint web site. We create a reference by using an existing site url, creating a site (collection) object for the url (impersonating the SystemAccount when we create it so that the tests will have access to lists etc), and finally getting the actual web site object for the url and storing it in HttpContext. Note: The HttpContext.Current we create is a dummy one but we do need an actual existing SharePoint site url.

Now this will get you started. Now you can create tests that call existing spaghetti code and not run into problems if this code calls SPContext.Current.

TIP 2

However, spaghetti SharePoint code often instatiates new SPSite objects left and right (to get access to lists for example) and this causes other problems.

Each time a new site is instantiated in the spaghetti code, it will "run" under the account you are using to run the automated tests. So not the SystemAccount we so nicely impersonated for the SPContext.Current above. Typically, the testing account will not have the necessary rights to do whatever the spaghetti code is trying to do (access lists more often that not). So, to solve this problem, run your test code with elevated privileges using code similar to:

SPSecurity.RunWithElevatedPrivileges(delegate() {
// automated testing code that calls the existing spaghetti code
});


Running with elevated priviliges ensures that the test has full rights to do whatever it wants. Evil but it gets the job done.

TIP 3

A final tip. If you test code is going to get items from lists, update them and then save them back to the list, you're probably going to hit some errors regarding a HTTP GET not being allowed to do an update. This has to do with the fake HttpContext created above and the SharePoint security model. I didn't dive to deep here but the problem can typically be resolved by using code along the lines of:

web.AllowUnsafeUpdates = true;
web.Update();


Here web is the web site you containing the lists you want to update. I just ended up adding this code to my automated tests if I hit the GET problem. But I'm sure there's a better solution out there (and if so, please tell me about it).

NOTES

1) A great thing about writing the automated tests was that I didn't have to attach my VS debugger to the wp3.exe process even once! I just ran (in debug mode if necesary) the tests, which speeded up my development significantly.

2) I initially built all my tests in vs2008 (using mstest) but at the end realized I couldn't run my test in acceptance environment without installing visual studio there completely! Since this is evil and NOT an option, I also made the tests run under NUnit as NUnit has a nice ui tool that can be easily installed (and deinstalled) on an acceptance environment. So I now had a test dll that could run in both testing frameworks and it cost very little extra work :-)

3) Of course, the above testing context is completely unsuited for testing any security functionality as it heavy handedly usurps full control to get the job done.


That's it for now. Happy SharePoint testing!