Tampering SharePoint assemblies part 2

Arseni Mourzenko
Founder and lead developer
170
articles
August 13, 2015
Tags: testing 6

In the pre­vi­ous ar­ti­cle, I ex­plained the con­text of a lega­cy Share­Point code which should be test­ed. For in­stance, re­gres­sion test­ing should cov­er meth­ods such as:

public string DoSomething(int whatever)
{
    var urls = SPContext.Current.Site.WebApplication.AlternateUrls;
    // Do something with those URIs.
}

For those who are un­fa­mil­iar with Share­Point, in or­der to be test­ed, the method re­quires ac­cess to a Share­Point farm, with the pre­req­ui­site of in­stalling and de­ploy­ing the code. As you can guess, that's not re­al­ly the spir­it of unit test­ing.

Now, what if we could run this method from unit tests by sim­ply do­ing:

SPWebApplicationExchanger.Instance.AlternateUrls.RemoveAll(c => true);
SPWebApplicationExchanger.Instance.AlternateUrls.Add(
    new SPAlternateUrl("http://demo.example.com/", SPUrlZone.Default));

var actual = new Demo().DoSomething(5);
var expected = "Expected result goes here";
Assert.AreEqual(expected, actual);

This is now—or will be soon—pos­si­ble.

Pre­vi­ous­ly, I ex­plained that ex­is­tent frame­works re­lat­ed to prox­ies and AOP can't help in a con­text of such tests, but tam­per­ing could be a so­lu­tion. The pro­to­type shows that it is in­deed tech­ni­cal­ly pos­si­ble to unit test such code.

The pre­vi­ous ar­ti­cle sug­gest­ed to tam­per the as­sem­blies on the fly. A few at­tempts have shown that this ap­proach is a fail­ure for two rea­sons:

The pro­to­type, on the oth­er hand, uses a dif­fer­ent ap­proach. Its build task is called when the test pro­ject is about to be built. From there, it match­es the proxy as­sem­blies to the orig­i­nal ones, com­bines them, and tam­pers the spe­cif­ic meth­ods. As a re­sult, a tam­pered method can still call class­es from the proxy as­sem­bly, and can use a data ex­chang­er to ac­tu­al­ly com­mu­ni­cate with tests.

For the as­sem­blies in GAC, an ad­di­tion­al step was re­quired, since CLR ig­nores lo­cal as­sem­blies if they ex­ist in GAC. Hope­ful­ly, bindingRedirect di­rec­tive made it pos­si­ble to cir­cum­vent this mech­a­nism by chang­ing the ver­sion of the tam­pered as­sem­bly.

The nice thing about this is that the same tech­nique can be ap­plied for oth­er as­sem­blies as well, in­clud­ing .NET Frame­work it­self. This makes it pos­si­ble to test code which, for in­stance, re­lies on System.IO.File by call­ing meth­ods such as File.WriteAllText while by­pass­ing ac­cess to the file sys­tem.

This may be the ul­ti­mate so­lu­tion for re­gres­sion test­ing of code writ­ten by in­ex­pe­ri­enced pro­gram­mers.