<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://blog.aabech.no/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Lars-Erik's blog</title>
    <link>https://blog.aabech.no/</link>
    <description>Ramblings about Umbraco, .net and JavaScript development. With a sprinkle of other stuff.</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <item>
      <guid isPermaLink="false">1216</guid>
      <link>https://blog.aabech.no/archive/more-efficient-integration-tests-with-umbraco/</link>
      <category>umbraco</category>
      <category>unit testing</category>
      <title>More efficient integration tests with Umbraco</title>
      <description>&lt;h2&gt;TLDR;&lt;/h2&gt;
&lt;p&gt;I made &lt;a href="https://www.nuget.org/packages/Umbraco.Community.Integration.Tests.Extensions"&gt;a package&lt;/a&gt; that removes the &amp;quot;database and Umbraco instance&amp;quot; per fixture constraint from &lt;a href="https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/"&gt;Umbraco's Integration Test library&lt;/a&gt;. It also enables reuse of seeded database snapshots to avoid executing the same setup / seeding over an over per fixture or test.&lt;/p&gt;
&lt;h2&gt;An itch&lt;/h2&gt;
&lt;p&gt;I've spent way too long waiting for similar setup code for my integration tests the last couple of years. The modern .NET version of &lt;a href="https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/"&gt;Umbraco's integration test library&lt;/a&gt; is super nice, and real effective with SQLite. But it's got a few constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All things Umbraco is instantiated and spun up per test, granted a configurable amount.&lt;/li&gt;
&lt;li&gt;Databases have to be &amp;quot;installed&amp;quot; and seeded for each TestFixture at the least.&lt;/li&gt;
&lt;li&gt;All test fixtures need to inherit from Umbraco's base classes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we spend a long time waiting for suff, even if it's only the little green box we care about:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1072/initial-umbraco-test-sequence.png" alt="Diagram of the hierarchy of a basic Umbraco integration test" /&gt;&lt;/p&gt;
&lt;p&gt;We're also stuck either juggling a singleton of some sorts and/or stuffing a lot of &amp;quot;units&amp;quot; into the same fixture.&lt;br /&gt;
Any notion of intermediate base classes quickly become a sore spot.&lt;/p&gt;
&lt;h2&gt;An idea&lt;/h2&gt;
&lt;p&gt;I had recently discovered, likely for the n-th time in my life, that NUnit supports namespace scoped setup fixtures. Those live as long as all that other tests in that namespace and deeper. They bring an opportunity to share initial state across several fixtures. I often find it nice to have several fixtures for the same systems under tests because it makes sense to group by use-case rather than tech.&lt;/p&gt;
&lt;p&gt;So I set out to see if I could hack together something that made Umbraco's base tests possible to use as setup fixtures rather than &amp;quot;base&amp;quot; fixtures. As things would have it it was early october, and the &lt;a href="https://candidcontributions.com/"&gt;CanCon gang hosted a virtual hackathon&lt;/a&gt; on a friday. I took the day &amp;quot;off&amp;quot; regular work and gave it a go.&lt;/p&gt;
&lt;h2&gt;Dark magic&lt;/h2&gt;
&lt;p&gt;In the end I've used all the dirty tricks you can imagine in today's .NET landscape. You'll find &lt;a href="https://www.nuget.org/packages/Lib.Harmony"&gt;Lib.Harmony&lt;/a&gt; for messing with Umbraco's tests' IL (Intermediate Language), and there's &lt;a href="https://www.nuget.org/packages/Castle.Core"&gt;Castle DynamicProxy&lt;/a&gt; doing a lot of fooling around with the NUnit test hierarchy. The Harmony bit can likely be removed by making a few slight changes to Umbraco's test core, but I wanted to get this working satisfactory before suggesting such changes.&lt;/p&gt;
&lt;p&gt;The bottom line is that those two tools let us run code before, after or even instead of inherited code. And that in turn enables all the following features.&lt;/p&gt;
&lt;h2&gt;A new way of composing tests&lt;/h2&gt;
&lt;p&gt;With the package I've cooked together we can leave the dark world of peach and purple above in favor of a way greener scenery like such:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1071/improved-umbraco-test-sequence.png" alt="Diagram of the hierarchy of an improved Umbraco integration test suite" /&gt;&lt;/p&gt;
&lt;p&gt;So without further ado, let's dig into how you can get there too.&lt;/p&gt;
&lt;h2&gt;Umbraco's attributes&lt;/h2&gt;
&lt;p&gt;We rely on Umbraco's &lt;code&gt;Tests:Database&lt;/code&gt; config and the &lt;code&gt;[UmbracoTest]&lt;/code&gt; attribute to provide databases (or not). The &lt;code&gt;Database&lt;/code&gt; option works almost as expected, but the four non-none options end up doing the same: they prepare &lt;em&gt;one&lt;/em&gt; db for the lifetime of the setup fixture.&lt;/p&gt;
&lt;h2&gt;[ExtendableSetUpFixture]&lt;/h2&gt;
&lt;p&gt;The first attribute you have to know is &lt;code&gt;[ExtendableSetUpFixture]&lt;/code&gt;. It's sole purpose is to enable the rest of them. Since it's inherited from &lt;code&gt;[SetUpFixture]&lt;/code&gt;, it tells NUnit we won't add any &lt;code&gt;[SetUp]&lt;/code&gt;, &lt;code&gt;[TearDown]&lt;/code&gt; or &lt;code&gt;[Test]&lt;/code&gt; methods in the marked class. But this is the attribute we'll add to any &lt;code&gt;UmbracoIntegrationTest&lt;/code&gt; or &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt;, and those have a bunch of each. That's why we have...&lt;/p&gt;
&lt;h2&gt;[MakeOneTimeLifecycle]&lt;/h2&gt;
&lt;p&gt;The first attribute in the package that &lt;em&gt;does something&lt;/em&gt;. &lt;code&gt;[MakeOneTimeLifecycle]&lt;/code&gt; lets you mark methods in otherwise not accessible base classes like &lt;code&gt;UmbracoTestBase&lt;/code&gt; to become &lt;code&gt;[OneTimeSetUp]&lt;/code&gt; methods rather than &lt;code&gt;[SetUp]&lt;/code&gt; methods, and &lt;code&gt;[TearDown]&lt;/code&gt; methods becomes &lt;code&gt;[OneTimeTearDown]&lt;/code&gt;.&lt;br /&gt;
Here's an example of what we'd feed it for an &lt;code&gt;UmbracoIntegrationTest&lt;/code&gt; inheritor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[UmbracoTest(Database=SchemaPerFixture, Boot=true)]
[ExtendableSetUpFixture]
[MakeOneTimeLifecycle(
    setUpNames: [nameof(UmbracoIntegrationTest.Setup), nameof(UmbracoIntegrationTest.SetUp_Logging)],
    tearDownNames: [
        nameof(UmbracoIntegrationTest.TearDown), nameof(UmbracoIntegrationTest.TearDownAsync), 
        nameof(UmbracoIntegrationTest.FixtureTearDown), nameof(UmbracoIntegrationTest.TearDown_Logging)
    ]
)]
public class MyLongLivedUmbracoSetUp : UmbracoIntegrationTest
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I found it a bother to write that long attribute all the time, so there's a derived one called:&lt;/p&gt;
&lt;h2&gt;[OneTimeUmbracoSetUp]&lt;/h2&gt;
&lt;p&gt;It makes it slightly less messy by letting us write as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[UmbracoTest(Database=SchemaPerFixture, Boot=true)]
[ExtendableSetUpFixture]
[OneTimeUmbracoSetUp]
public class MyLongLivedUmbracoSetUp : UmbracoIntegrationTest
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note however that the current version doesn't come with a 100% compatible one for &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt; or &lt;code&gt;ManagementApiTest&amp;lt;&amp;gt;&lt;/code&gt;, but it's easy to make one yourself, and they're likely suddenly in the package.&lt;/p&gt;
&lt;p&gt;Now if you're into hacking around the environment and hidden, protected members lurking around in memory, we're quite alike. But when doing professional work that's often not what we're paid for. So even though the above code will start up a perfectly fine Umbraco instance with a SQLite database it doesn't do much good. We need to get at the &lt;code&gt;IServiceProvider&lt;/code&gt; somehow...&lt;/p&gt;
&lt;h2&gt;[InjectionProvider(string)] and [ServiceProvider]&lt;/h2&gt;
&lt;p&gt;There is a limit to how many attributes we'll stick on our setup fixtures, I promise. I complained about intermediate base classes earlier, but a few for attribute consolidation is cool enough. OK, these two are a basic re-usable one and a &amp;quot;defaulted&amp;quot; one. The &lt;code&gt;[InjectionProvider(string)]&lt;/code&gt; attribute stores a reference to a property on the setup fixture that exposes an &lt;code&gt;IServiceProvider&lt;/code&gt; instance. Umbraco's base classes exposea property called &lt;code&gt;Services&lt;/code&gt;, and &lt;code&gt;[ServiceProvider]&lt;/code&gt; is just a derived &lt;code&gt;[InjectionProvider(nameof(Services))]&lt;/code&gt;. To be specific, the service provider reference goes into the test context property bag as a factory method.&lt;br /&gt;
By adding one to our growing tower we're ready to meet our first actual &lt;em&gt;test fixture&lt;/em&gt; enjoying the power of:&lt;/p&gt;
&lt;h2&gt;[Inject(string)]&lt;/h2&gt;
&lt;p&gt;To enjoy the benefits of our stored service provider reference, we must expose an instance method on our &amp;quot;scoped&amp;quot; test fixture that accepts services. We could've used constructor injection, but that's already &amp;quot;in use&amp;quot; by NUnit &lt;code&gt;[TestFixture(args)]&lt;/code&gt; functionality. Once you start composing tests like this package allows, you can get &lt;em&gt;much more value&lt;/em&gt; from things like that. So a void method will have to do. A fresh &amp;quot;standalone&amp;quot; and &amp;quot;scoped&amp;quot; test fixture will look like this:&lt;/p&gt;
&lt;p&gt;using Same.Namespace.As.SetupFixture;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Inject(nameof(Inject))]
public class LookMaImFreeAsABird
{
    IDataTypeService dataTypeService = null!;

    public void Inject(IDataTypeService dataTypeService)
    {
        this.dataTypeService = dataTypeService;
    }

    [Test]
    public void FiddleWithDataTypeA()
    {
        // ...
    }

    [Test]
    public void CreateDataTypeB()
    {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the beauty of &lt;em&gt;no forced ctors&lt;/em&gt;, &lt;em&gt;no base class&lt;/em&gt; and &lt;em&gt;no overrides&lt;/em&gt; in that class! Not to mention, you can have &lt;em&gt;several&lt;/em&gt; of those!&lt;/p&gt;
&lt;h2&gt;Interlude: Transactions&lt;/h2&gt;
&lt;p&gt;With the above setup the individual fixtures are free to create &lt;code&gt;ICoreScope&lt;/code&gt; instances from Umbraco and commit or dispose them as you wish. Just writing three sentences about it here doesn't really give the method justice. Suffice to say, just give it a go!&lt;br /&gt;
However it won't work when we use &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt;. There's no way to spread a core scope across threads, and there's not even a way to spread gool old .NET transaction scopes across. I guess you could go full old school and bring DCOM into the picture, but after a lot of failed attempts I finally stumbled over an obvious solution.&lt;/p&gt;
&lt;h2&gt;Snapshots&lt;/h2&gt;
&lt;p&gt;The final itch has taken me the longest, and even longer to stuff into an attribute rather than yet another base class. I must admit it took a desperate (but accurate) final plea with ChatGPT 5.1 to see the obvious solution just lying there to implement. All database engines, or at least the two I've implemented, support some sort of fast backup. &lt;code&gt;VACUUM INTO&lt;/code&gt; in SQLite and &lt;code&gt;CREATE/RESTORE DATABASE AS SNAPSHOT&lt;/code&gt; in SQL Server.&lt;/p&gt;
&lt;p&gt;I had two intermediate base classes for a few weeks, but this last weekend I ran over the goal line:&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;[ReusableDatabase(type, string)]&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If you configure &lt;code&gt;Tests:Database:DatabaseType&lt;/code&gt; in your &lt;code&gt;appSettings.Tests.Local.json&lt;/code&gt; and set any of the non-none values in the &lt;code&gt;[UmbracoTest]&lt;/code&gt; database parameter, you can also add &lt;code&gt;[ReusableDatabase(typeof(SeedClass), nameof(SeedClass.Configure))]&lt;/code&gt; to get a new type of &lt;code&gt;ITestDatabase&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As of writing it requires a method to be implemented in the setup fixture, or on some supporting type. Its mission is to configure whether the database needs a fresh seed and how to seed the initial data for all the tests. Only if we say so, the database is installed and re-seeded. For all other scopes it's just restored from that initial snapshot.&lt;/p&gt;
&lt;p&gt;The seeding can be done in any way you please. My favorite is importing things using uSync, and that might just become another blog post. For simplicity let's say we're testing variants of property editors based on a datatype we want all tests to start with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[UmbracoTest(
    Database = UmbracoTestOptions.Database.NewSchemaPerTest,
    Boot = true,
    Logger = UmbracoTestOptions.Logger.Console
)]
[ExtendableSetUpFixture]
[OneTimeUmbracoSetUp]
[ServiceProvider]
[ReusableDatabase(nameof(ConfigureSeeding))]
public class ReusedDbAttributeSetUp : UmbracoIntegrationTest
{
    public static void ConfigureSeeding(ReusableTestDatabaseOptions options)
    {
        options.NeedsNewSeed = _ =&amp;gt; Task.FromResult(true);
        options.SeedData = async (services) =&amp;gt;
        {
            await TestContext.Progress.WriteLineAsync(&amp;quot;Creating datatype&amp;quot;);
            await services.GetRequiredService&amp;lt;IDataTypeService&amp;gt;().CreateAsync(
                new DataType
                (
                    new TextboxPropertyEditor
                    (
                        services.GetRequiredService&amp;lt;IDataValueEditorFactory&amp;gt;(),
                        services.GetRequiredService&amp;lt;IIOHelper&amp;gt;()
                    ),
                    services.GetRequiredService&amp;lt;IConfigurationEditorJsonSerializer&amp;gt;()
                )
                {
                    Name = &amp;quot;A seeded textbox&amp;quot;
                },
                Umbraco.Cms.Core.Constants.Security.SuperUserKey
            );
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The method has to be static void. If it's on the setup fixture you can omit the type from the attribute parameters.&lt;br /&gt;
Now all tests within this &amp;quot;scope&amp;quot; will have access to the seeded textbox. Such seeding code might quickly grow out of hand though, and that's why I prefer just importing the content schema from the web project in the same repo as my tests.&lt;/p&gt;
&lt;h2&gt;Which ones to choose&lt;/h2&gt;
&lt;p&gt;As you've hopefully realized by now you can introduce really slim intermediate base classes that set up the initial environment for a bunch of use-cases and scenarios. Leaving your actual test fixtures free to be composed just as you wish, and with all the features of NUnit at your disposal.&lt;/p&gt;
&lt;p&gt;I'm sure you can see that the last setup fixture example above makes for a nice base class to avoid repeating that tower of attributes. The &lt;a href="https://github.com/lars-erik/NUnitCompositionWithUmbraco/tree/main/UmbracoTestsComposition"&gt;repository has a few examples using a few or all of the attributes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The final trick I haven't disclosed above is how to do a &amp;quot;mid-fixture&amp;quot; rollback. The reusable database implementations have a &amp;quot;RestoreSnapshot&amp;quot; method, so in a &lt;code&gt;[TearDown]&lt;/code&gt; you can go:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;await serviceProvider.GetRequiredService&amp;lt;IReusableDatabase&amp;gt;().RestoreSnapshot();&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;That makes it so that whatever test comes next it's unaffected by the changes you've made. This is of course what happens during &lt;code&gt;[OneTimeSetUp]&lt;/code&gt; for any setup fixture using &lt;code&gt;[ReusableDatabase]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to try this out, I recommend going with &lt;code&gt;[ReusableDatabase]&lt;/code&gt; (and uSync) for everything that use more than 10ish artifacts like data and content types, not to mention content. Whenever you want to run changes through the Management API, or definitely if you want to test how all your sites' elements look in the Delivery API. It's a no-brainer when you go full integration with &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt; and more than one HTTP call.&lt;/p&gt;
&lt;p&gt;If all your tests have all they need in a fresh Umbraco database, but they all mutate it so you need cleanup, it'll be blazing fast if you can use &lt;code&gt;ICoreScope&lt;/code&gt; with autocommit off. As long as you don't need to test via HTTP endpoints, this is a better option than restoring the snapshot. Using a snapshot restore between fixtures however is still a nice option to keep around, so I still opt for &lt;code&gt;[ReusableDatabase]&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Want more?&lt;/h2&gt;
&lt;p&gt;I'm definitely going to be in &amp;quot;only fix what breaks for me&amp;quot; mode for a little while, as this was just gonna be a hacktoberfest weekend project. Turned out to take too much of my spare time for one and a half month instead.&lt;/p&gt;
&lt;p&gt;But I honestly believe this'll save some trees if applied well to CI pipelines running all day long.&lt;br /&gt;
And I've gone from 90 sec to 26 sec to execute a full test suite, a lot of which would run for &lt;em&gt;any&lt;/em&gt; filter.&lt;/p&gt;
&lt;p&gt;The package is currently listed as a beta (pre-release) on nuget and if it doesn't crash much I might just promote it to full visibility. It's a bit furry on the edges and has a bit of legacy to it (already), but if you stick the namespaces in the project file and leave the stray base classes alone I think you'll be good.&lt;/p&gt;
&lt;p&gt;All this is to say I'd love for conversation about what it solves and if it could do it better. I'd love even more to throw out a bit of the code because Umbraco suddenly fixed the core two issues. Let me hear from you if you try it out. Feel free to clone the code and mess about, although I reserve the right to refactor the mess myself that beautiful day I get to keep going with this. &lt;/p&gt;
&lt;p&gt;Looking forward to hearing how (and if) you like it. Hope you have a go!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/lars-erik/NUnitCompositionWithUmbraco"&gt;Source repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Umbraco.Community.Integration.Tests.Extensions"&gt;Nuget package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/"&gt;Umbraco Cms Tests Integration package&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
      <pubDate>Tue, 25 Nov 2025 07:11:08 Z</pubDate>
      <a10:updated>2025-11-25T07:11:08Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1212</guid>
      <link>https://blog.aabech.no/archive/migrating-umbraco-forms-from-7-to-modern/</link>
      <category>umbraco</category>
      <title>Migrating Umbraco Forms from 7 to modern</title>
      <description>&lt;h3&gt;No migration, no cry&lt;/h3&gt;
&lt;p&gt;There is no uSync Migrations extension for moving forms from 7 to modern.
There isn't even a uSync Forms for V7.
However the serialization format for forms on disk didn't change (much?) from 7 to 8, so we can do a trick. (Thanks to Kevin for the tip)&lt;br /&gt;
We can do 7 to 8, then 8 to modern.&lt;br /&gt;
This applies to files. I have not tried with the forms in database method, but that should be possible to do with database migrations from 7 to 8.&lt;/p&gt;
&lt;h3&gt;7 to 8&lt;/h3&gt;
&lt;p&gt;Create an empty V8 site and install the following nuget packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UmbracoForms&lt;/li&gt;
&lt;li&gt;uSync.Forms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To &amp;quot;migrate&amp;quot; your forms from 7 to 8 you just copy the files over. 😁&lt;br /&gt;
Your source files should be in &lt;code&gt;/App_Data/UmbracoForms/Data&lt;/code&gt;.&lt;br /&gt;
The target should be the same in a V8 site.&lt;/p&gt;
&lt;p&gt;When your files are copied you're ready to go into the backoffice.&lt;br /&gt;
Go to Settings \ uSync and click Export.&lt;/p&gt;
&lt;p&gt;You should now have uSync files for the forms under &lt;code&gt;/uSync/v8/Forms&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;8 to modern&lt;/h3&gt;
&lt;p&gt;Install uSync.Forms in your modern site.&lt;br /&gt;
Copy your uSync forms files from the V8 site into &lt;code&gt;/uSync/v9/Forms&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The uSync files will have prevalues in a &lt;code&gt;parsedPreValues&lt;/code&gt; field instead of &lt;code&gt;preValues&lt;/code&gt;.&lt;br /&gt;
uSync Forms import in modern does not like this.&lt;br /&gt;
So I made a small powershell script that does the necessary &amp;quot;migration&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;param(
    $path
)

$files = Get-ChildItem -Path $path -File

$files | % {
    $fileInfo = $_
    $doc = [xml](Get-Content $fileInfo.FullName)
    $pageNode = $doc.DocumentElement.SelectSingleNode(&amp;quot;Pages&amp;quot;)
    $origCData = $pageNode.FirstChild
    $pages = [array]($origCData.InnerText | ConvertFrom-Json)

    $results = $pages | % {
        $page = $_
        $page.fieldSets | % {
            $fieldSet = $_
            $fieldSet.containers | % {
                $container = $_
                $container.fields | % {
                    $field = $_
                    if ($field.parsedPrevalues.Length -gt 0) {
                        $field.preValues = $field.parsedPrevalues
                    }
                    $field.psobject.Properties.Remove(&amp;quot;parsedPreValues&amp;quot;)
                }
            }
        }
    }

    $cdata = $doc.CreateCDataSection((ConvertTo-Json -depth 99 -inputobject $pages))
    $replaced = $pageNode.ReplaceChild($cdata, $origCData)

    $doc.Save($fileInfo.FullName)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I called the script &lt;code&gt;fix-usync-forms.ps1&lt;/code&gt; and we can call it as such:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./fix-usync-forms.ps1 [path-to-site]/uSync/v9/Forms&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That's it!&lt;/p&gt;
&lt;p&gt;We can now go into the backoffice in our modern site.&lt;br /&gt;
Go to Settings \ uSync and click &amp;quot;Import&amp;quot; under Forms.&lt;/p&gt;
&lt;h3&gt;Success&lt;/h3&gt;
&lt;p&gt;Now all that's left is porting your potential code and views.&lt;/p&gt;
&lt;p&gt;Happy migrating! 🙃&lt;/p&gt;
</description>
      <pubDate>Wed, 28 Feb 2024 11:58:06 Z</pubDate>
      <a10:updated>2024-02-28T11:58:06Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1206</guid>
      <link>https://blog.aabech.no/archive/unit-testing-the-iis-url-rewrite-module/</link>
      <category>unit testing</category>
      <title>Unit Testing the IIS URL Rewrite Module</title>
      <description>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;I can't count on two hands the amount of times I've messed up some IIS redirect rule on our sites.
&amp;quot;We need all these 50 old top traffic drivers from the old site, and of course we want
five domains per country in case someone mistypes it and Google don't show us as first hit.&lt;br /&gt;
Oh, and by the way, everybody should be able to use these three slugs for our new campaigs.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bloody marketeers!&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;Wouldn't it be nice if we could write some automated tests that verifies that we've configured
the UrlRewriteModule correctly? I've been half-heartedly looking for a way to do this with 
the proprietary .NET Framework IIS Module, but recently realized while researching it that
it's a public middleware in .NET Core.&lt;br /&gt;
This means we &lt;em&gt;can&lt;/em&gt; start to write unit tests against it, and verify that stuff we want to
use with our old .NET Framework sites &lt;em&gt;will&lt;/em&gt; work even if we test for - and redirect to domains.&lt;/p&gt;
&lt;h2&gt;Test dependencies&lt;/h2&gt;
&lt;p&gt;Since I'm testing the config of a .NET Framework app, I just add a separate .NET Core 6 NUnit test project.
Its sole purpose is verifying these redirects.&lt;/p&gt;
&lt;p&gt;All the dependencies are nuget packages, but I just went and used some fixed paths to the SDK for now.
Here are the test project dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;Microsoft.NET.Test.Sdk&amp;quot; Version=&amp;quot;17.1.0&amp;quot; /&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;Moq&amp;quot; Version=&amp;quot;4.18.2&amp;quot; /&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;NUnit&amp;quot; Version=&amp;quot;3.13.3&amp;quot; /&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;NUnit3TestAdapter&amp;quot; Version=&amp;quot;4.2.1&amp;quot; /&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;NUnit.Analyzers&amp;quot; Version=&amp;quot;3.3.0&amp;quot; /&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;Verify.NUnit&amp;quot; Version=&amp;quot;17.10.2&amp;quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;

&amp;lt;ItemGroup&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.AspNetCore.Http&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.AspNetCore.Http.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.AspNetCore.Http.Abstractions&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.AspNetCore.Http.Abstractions.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.AspNetCore.Rewrite&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.AspNetCore.Rewrite.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.AspNetCore.Http.Features&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.AspNetCore.Http.Features.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.AspNetCore.Hosting.Abstractions&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.AspNetCore.Hosting.Abstractions.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.Extensions.Features&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.Extensions.Features.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.Extensions.Hosting.Abstractions&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.Extensions.Hosting.Abstractions.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.Extensions.Logging.Abstractions&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.Extensions.Logging.Abstractions.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.Extensions.FileProviders.Abstractions&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.Extensions.FileProviders.Abstractions.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
    &amp;lt;Reference Include=&amp;quot;Microsoft.Extensions.Options&amp;quot;&amp;gt;
        &amp;lt;HintPath&amp;gt;C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.9\Microsoft.Extensions.Options.dll&amp;lt;/HintPath&amp;gt;
    &amp;lt;/Reference&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have them, we can start setting up a totally mocked HttpContext and look at
what the IIS UrlRewritingModule middleware does.&lt;/p&gt;
&lt;h2&gt;The middleware&lt;/h2&gt;
&lt;p&gt;In order to use the IIS UrlRewriteModule in ASP.NET Core we do &lt;code&gt;app.UseRewriter(RewriteOptions)&lt;/code&gt; and on the &lt;code&gt;RewriteOptions&lt;/code&gt;
we do &lt;code&gt;.AddIISUrlRewrite(FileProvider, &amp;quot;web.config&amp;quot;)&lt;/code&gt;. All these two things do is camouflaging a middleware and parsing of the config.&lt;br /&gt;
What we need in our test is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// omitted quite a few web hosting mocks
var reader = // some way to read our config as a web config file
var options = new RewriteOptions().AddIISUrlRewrite(reader);
var middleware = new RewriteMiddleware(_ =&amp;gt; Task.CompletedTask, Mock.Of&amp;lt;IWebHostEnvironment&amp;gt;(), loggerFactory, new OptionsWrapper&amp;lt;RewriteOptions&amp;gt;(options))
middleware.Invoke(httpContext);
Verify(state);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;All necessary code is at the bottom of this post.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Reading the configuration&lt;/h2&gt;
&lt;p&gt;We usually put our rewrite rules in a separate file in the web project. It's linked from the web.config file
using the configSource attribute as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;system.webServer&amp;gt;
    &amp;lt;rewrite&amp;gt;
        &amp;lt;rules configSource=&amp;quot;config\RewriteRules.config&amp;quot; /&amp;gt;
    &amp;lt;/rewrite&amp;gt;
&amp;lt;/system.webServer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To be frank it's actually done on publish using a transform file, so we don't have it configured when working locally.&lt;br /&gt;
The rules are then managed in a file looking like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;
&amp;lt;rules&amp;gt;
    &amp;lt;rule enabled=&amp;quot;true&amp;quot; name=&amp;quot;test&amp;quot;&amp;gt;
        &amp;lt;match url=&amp;quot;(.*)&amp;quot; /&amp;gt;
        &amp;lt;conditions logicalGrouping=&amp;quot;MatchAll&amp;quot;&amp;gt;
            &amp;lt;add input=&amp;quot;{HTTP_URL}&amp;quot; pattern=&amp;quot;.*abc.*&amp;quot; matchType=&amp;quot;Pattern&amp;quot;  /&amp;gt;
        &amp;lt;/conditions&amp;gt;
        &amp;lt;action type=&amp;quot;Redirect&amp;quot; redirectType=&amp;quot;Found&amp;quot; url=&amp;quot;/tada&amp;quot; /&amp;gt;
    &amp;lt;/rule&amp;gt;
&amp;lt;/rules&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However the &lt;code&gt;AddIISUrlRewrite&lt;/code&gt; extension for the &lt;code&gt;RewriteOptions&lt;/code&gt; require a stream of a full web.config file.
I've just hacked together a small factory method that wraps our config in a slim web.config envelope:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var path = Path.Join(AppDomain.CurrentDomain.BaseDirectory, @&amp;quot;..\..\..\..\Customer.Web\config\rewriterules.config&amp;quot;);
var contents = File.ReadAllText(path)
    .Replace(&amp;quot;&amp;lt;?xml version=\&amp;quot;1.0\&amp;quot; encoding=\&amp;quot;utf-8\&amp;quot; ?&amp;gt;&amp;quot;, &amp;quot;&amp;quot;);
var fullContents = &amp;quot;&amp;lt;?xml version=\&amp;quot;1.0\&amp;quot; encoding=\&amp;quot;utf-8\&amp;quot; ?&amp;gt;&amp;lt;configuration&amp;gt;&amp;lt;system.webServer&amp;gt;&amp;lt;rewrite&amp;gt;&amp;quot; + 
                    contents +
                   &amp;quot;&amp;lt;/rewrite&amp;gt;&amp;lt;/system.webServer&amp;gt;&amp;lt;/configuration&amp;gt;&amp;quot;;
var reader = new StringReader(fullContents);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It can now be passed to &lt;code&gt;AddIISUrlRewrite&lt;/code&gt; and we're good to go.&lt;/p&gt;
&lt;h2&gt;A mock HttpContext&lt;/h2&gt;
&lt;p&gt;The UrlRewriteModule reads from the &lt;code&gt;HttpRequest&lt;/code&gt; instance and possibly writes to the &lt;code&gt;HttpResponse&lt;/code&gt; instance.
We need to mock both these. However the stuff we want to &lt;em&gt;vary&lt;/em&gt; is all he URL data, 
so we pass that to a (fairly large) factory method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private static HttpContext CreateHttpContext(Uri uri)
{
    var ctx = Mock.Of&amp;lt;HttpContext&amp;gt;();

    var resp = Mock.Of&amp;lt;HttpResponse&amp;gt;();
    var headers = new HeaderDictionary();
    Mock.Get(resp).Setup(x =&amp;gt; x.Headers).Returns(headers);
    Mock.Get(ctx).Setup(x =&amp;gt; x.Response).Returns(resp);

    var req = Mock.Of&amp;lt;HttpRequest&amp;gt;();
    Mock.Get(ctx).Setup(x =&amp;gt; x.Request).Returns(req);
    Mock.Get(req).Setup(x =&amp;gt; x.Path).Returns(new PathString(uri.AbsolutePath));
    Mock.Get(req).Setup(x =&amp;gt; x.Scheme).Returns(uri.Scheme);
    Mock.Get(req).Setup(x =&amp;gt; x.Host).Returns(new HostString(uri.Host, uri.Port));
    Mock.Get(req).Setup(x =&amp;gt; x.PathBase).Returns(new PathString(&amp;quot;/&amp;quot;));
    Mock.Get(req).Setup(x =&amp;gt; x.QueryString).Returns(new QueryString(uri.Query));

    var variableFeature = Mock.Of&amp;lt;IServerVariablesFeature&amp;gt;();
    var features = new FeatureCollection();
    features.Set(variableFeature);
    Mock.Get(ctx).Setup(x =&amp;gt; x.Features).Returns(features);

    Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTP_HOST&amp;quot;]).Returns(uri.Host);
    Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTP_URL&amp;quot;]).Returns(uri.AbsolutePath);
    Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTPS&amp;quot;]).Returns(uri.Scheme == &amp;quot;https&amp;quot; ? &amp;quot;on&amp;quot; : &amp;quot;off&amp;quot;);
    return ctx;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;What to verify?&lt;/h2&gt;
&lt;p&gt;I was initially trying to verify the &lt;code&gt;HttpResponse&lt;/code&gt;. We can do that and test the StatusCode and headers etc.
However, I was &lt;code&gt;Console.WriteLine&lt;/code&gt;ing all the logging and it turns out the UrlRewrite middleware logs 
perfectly nice descriptions of what happens. It also (usually) stops at the rule that handles the URL,
so if we take the last two outputs, we're quite good to go:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Request matched current UrlRewriteRule 'test'.
Location header '//tada' with status code '302'.

Request did not match current rule 'test'.
Current url is http://localhost:80/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If I could have my will, I'd like something like this, and I can have it. :)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/abc =&amp;gt; Request matched current UrlRewriteRule 'test'. Location header '//tada' with status code '302'.
http://localhost/ =&amp;gt; Request did not match current rule 'test'. Current url is http://localhost:80/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So what &lt;code&gt;loggerFactory&lt;/code&gt; sets up is an instance that keeps a local message log, and of course write to the console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RedirectLogger : ILogger
{
    public List&amp;lt;string&amp;gt; Messages { get; } = new List&amp;lt;string&amp;gt;();

    public void Log&amp;lt;TState&amp;gt;(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func&amp;lt;TState, Exception?, string&amp;gt; formatter)
    {
        var output = formatter(state, exception);
        output = output.Replace(&amp;quot;Request is done processing. &amp;quot;, &amp;quot;&amp;quot;);
        output = output.Replace(&amp;quot;Request is continuing in applying rules. &amp;quot;, &amp;quot;&amp;quot;);
        Console.WriteLine(output);
        Messages.Add(output);
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }

    public IDisposable BeginScope&amp;lt;TState&amp;gt;(TState state)
    {
        throw new NotImplementedException();
    }

    public override string ToString()
    {
        return String.Join(&amp;quot; &amp;quot;, Messages);
    }

    public string Last(int count = 2)
    {
        return String.Join(&amp;quot; &amp;quot;, Messages.TakeLast(count));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I keep that instance around for the test to verify what was written to it.&lt;/p&gt;
&lt;h2&gt;Trying it together&lt;/h2&gt;
&lt;p&gt;The last piece here is &lt;a href="https://github.com/VerifyTests/Verify"&gt;Verify&lt;/a&gt; which we can use for larger &amp;quot;assertEquals&amp;quot; blocks.&lt;/p&gt;
&lt;p&gt;I'd like one big report of different URLs I expect hit the server, and then the result for each url.
So I set up a bunch of testcases like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static Uri[] TestCases() =&amp;gt; new[]
{
    new Uri(&amp;quot;http://localhost/abc&amp;quot;),
    new Uri(&amp;quot;https://localhost/abc&amp;quot;),
    new Uri(&amp;quot;http://localhost/tadabcada&amp;quot;),
    new Uri(&amp;quot;http://localhost/&amp;quot;),
    new Uri(&amp;quot;http://localhost/tada&amp;quot;)
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can loop over those and verify a report in our test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
public async Task Handles_All_Rules()
{
    List&amp;lt;string&amp;gt; results = new List&amp;lt;string&amp;gt;();
    foreach(var uri in TestCases())
    {
        var ctx = CreateHttpContext(uri);

        redirectLogger.Messages.Clear();
        await middleware.Invoke(ctx);

        results.Add(uri + &amp;quot; =&amp;gt; &amp;quot; + redirectLogger);
    }

    await Verify(String.Join(Environment.NewLine, results));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our output becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/abc =&amp;gt; Request matched current UrlRewriteRule 'test'. Location header '//tada' with status code '302'.
https://localhost/abc =&amp;gt; Request matched current UrlRewriteRule 'test'. Location header '//tada' with status code '302'.
http://localhost/tadabcada =&amp;gt; Request matched current UrlRewriteRule 'test'. Location header '//tada' with status code '302'.
http://localhost/ =&amp;gt; Request did not match current rule 'test'. Current url is http://localhost:80/
http://localhost/tada =&amp;gt; Request did not match current rule 'test'. Current url is http://localhost:80/tada
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Fairly easy to read, and now we'll blow up if ever we break our rules.&lt;/p&gt;
&lt;p&gt;I think the readability can be tweaked for even better readability and intuition, but for now I'm super happy I can test it.
There are tons of other cases I haven't covered of course, but this is a very good starting place.&lt;/p&gt;
&lt;p&gt;One quirk I've noticed is that the location header always gets a leading slash, but i think that's due to running in a test.&lt;/p&gt;
&lt;p&gt;Hope you like it.&lt;br /&gt;
I'd love comments about usage or extensions. :)&lt;/p&gt;
&lt;h2&gt;All the code&lt;/h2&gt;
&lt;p&gt;Here's all the code reading the config from a resource file instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.AspNetCore.Rewrite;
using Moq;
using DiffEngine;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using NUnit.Framework;

namespace UrlRewrite.Tests
{
    public class UrlRewriting
    {
        public static Uri[] TestCases() =&amp;gt; new[]
        {
            new Uri(&amp;quot;http://localhost/abc&amp;quot;),
            new Uri(&amp;quot;https://localhost/abc&amp;quot;),
            new Uri(&amp;quot;http://localhost/tadabcada&amp;quot;),
            new Uri(&amp;quot;http://localhost/&amp;quot;),
            new Uri(&amp;quot;http://localhost/tada&amp;quot;)
        };

        [Test]
        public async Task Handles_All_Rules()
        {
            List&amp;lt;string&amp;gt; results = new List&amp;lt;string&amp;gt;();
            foreach(var uri in TestCases())
            {
                var ctx = CreateHttpContext(uri);

                redirectLogger.Messages.Clear();
                await middleware.Invoke(ctx);

                results.Add(uri + &amp;quot; =&amp;gt; &amp;quot; + redirectLogger);
            }

            await Verify(String.Join(Environment.NewLine, results));
        }

        private RewriteMiddleware middleware = null!;
        private RedirectLogger redirectLogger = null!;

        [SetUp]
        public void Setup()
        {
            DiffTools.UseOrder(DiffTool.VisualStudio, DiffTool.Rider, DiffTool.VisualStudioCode);

            var options = CreateOptions(GetType().Assembly.GetManifestResourceStream(&amp;quot;UrlRewrite.Tests.testroutes.xml&amp;quot;)!);
            redirectLogger = new RedirectLogger();
            var loggerFactory = CreateLoggerFactory(redirectLogger);
            middleware = CreateMiddleware(loggerFactory, options);
        }

        private static HttpContext CreateHttpContext(Uri uri)
        {
            var ctx = Mock.Of&amp;lt;HttpContext&amp;gt;();

            var resp = Mock.Of&amp;lt;HttpResponse&amp;gt;();
            var headers = new HeaderDictionary();
            Mock.Get(resp).Setup(x =&amp;gt; x.Headers).Returns(headers);
            Mock.Get(ctx).Setup(x =&amp;gt; x.Response).Returns(resp);

            var req = Mock.Of&amp;lt;HttpRequest&amp;gt;();
            Mock.Get(ctx).Setup(x =&amp;gt; x.Request).Returns(req);
            Mock.Get(req).Setup(x =&amp;gt; x.Path).Returns(new PathString(uri.AbsolutePath));
            Mock.Get(req).Setup(x =&amp;gt; x.Scheme).Returns(uri.Scheme);
            Mock.Get(req).Setup(x =&amp;gt; x.Host).Returns(new HostString(uri.Host, uri.Port));
            Mock.Get(req).Setup(x =&amp;gt; x.PathBase).Returns(new PathString(&amp;quot;/&amp;quot;));
            Mock.Get(req).Setup(x =&amp;gt; x.QueryString).Returns(new QueryString(uri.Query));

            var variableFeature = Mock.Of&amp;lt;IServerVariablesFeature&amp;gt;();
            var features = new FeatureCollection();
            features.Set(variableFeature);
            Mock.Get(ctx).Setup(x =&amp;gt; x.Features).Returns(features);

            Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTP_HOST&amp;quot;]).Returns(uri.Host);
            Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTP_URL&amp;quot;]).Returns(uri.AbsolutePath);
            Mock.Get(variableFeature).Setup(x =&amp;gt; x[&amp;quot;HTTPS&amp;quot;]).Returns(uri.Scheme == &amp;quot;https&amp;quot; ? &amp;quot;on&amp;quot; : &amp;quot;off&amp;quot;);
            return ctx;
        }

        private RewriteMiddleware CreateMiddleware(ILoggerFactory loggerFactory, RewriteOptions options)
        {
            return new RewriteMiddleware(_ =&amp;gt; Task.CompletedTask, Mock.Of&amp;lt;IWebHostEnvironment&amp;gt;(), loggerFactory, new OptionsWrapper&amp;lt;RewriteOptions&amp;gt;(options));
        }

        private static ILoggerFactory CreateLoggerFactory(ILogger logger)
        {
            var loggerFactory = Mock.Of&amp;lt;ILoggerFactory&amp;gt;();
            Mock.Get(loggerFactory).Setup(x =&amp;gt; x.CreateLogger(It.IsAny&amp;lt;string&amp;gt;())).Returns(logger);
            return loggerFactory;
        }

        private static RewriteOptions CreateOptions(Stream stream)
        {
            using var reader = new StreamReader(stream);
            var options = new RewriteOptions().AddIISUrlRewrite(reader);
            options.StaticFileProvider = Mock.Of&amp;lt;IFileProvider&amp;gt;();
            return options;
        }
    }

    public class RedirectLogger : ILogger
    {
        public List&amp;lt;string&amp;gt; Messages { get; } = new List&amp;lt;string&amp;gt;();

        public void Log&amp;lt;TState&amp;gt;(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func&amp;lt;TState, Exception?, string&amp;gt; formatter)
        {
            var output = formatter(state, exception);
            output = output.Replace(&amp;quot;Request is done processing. &amp;quot;, &amp;quot;&amp;quot;);
            output = output.Replace(&amp;quot;Request is continuing in applying rules. &amp;quot;, &amp;quot;&amp;quot;);
            Console.WriteLine(output);
            Messages.Add(output);
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public IDisposable BeginScope&amp;lt;TState&amp;gt;(TState state)
        {
            throw new NotImplementedException();
        }

        public override string ToString()
        {
            return String.Join(&amp;quot; &amp;quot;, Messages);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Mon, 19 Sep 2022 13:09:15 Z</pubDate>
      <a10:updated>2022-09-19T13:09:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1205</guid>
      <link>https://blog.aabech.no/archive/brushing-up-old-apis-with-castle-dynamicproxy/</link>
      <category>interop</category>
      <category>dynamic proxies</category>
      <title>Brushing up old APIs with Castle DynamicProxy</title>
      <description>&lt;h2&gt;Old APIs?&lt;/h2&gt;
&lt;p&gt;Recently I had the pleasure of rebuilding a B2B ecom site for a local butchery.
It's never been properly integrated with their ERP, but the new site is.
The front-end is built using Umbraco 9 and Vendr and hosted on Azure, 
while the integration side is an ASPNET Core WebAPI solution running closer to the ERP-system.
They are using an ERP system that's been around since the nineties.
Of course it's been upgraded and modernized throughout the years, 
but the core APIs are still the same as they've been since then.
The maturity of the software says enough about the architecture.
We can't say that it didn't work, wasn't maintainable or not durable.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;However&lt;/em&gt;, programming styles have changed over the years.
Back then and earlier it was perfectly sensible to inform function callers about
runtime errors and exceptions using integer return codes.
Executable binaries still do, though most just exit with a 0.
Usually a 0 means all is well while any non-zero value means something didn't go well.&lt;/p&gt;
&lt;p&gt;Another thing with such unmanaged APIs is that we have to go through interop.
That means we have to mind releasing absolutely all resources we instantiate.&lt;/p&gt;
&lt;p&gt;Hence every managed example for this ancient (forgive me) API goes something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Server server = new Server();

int result = server.LogOn();
if (result != 0) {
    goto Fail;
}

ComponentA component = (ComponentA)server.Components[(int)ComponentTypesEnum.ComponentA];
result = component.SetupData();
if (result != 1) {
    goto Fail;
}

result = component.BindData();
if (result != -1) {
    goto Fail;
}

result = component.DoFirstThing();
if (result != 0) {
    goto Fail;
}

result = component.DoSecoondThing();
if (result != 0) {
    goto Fail;
}

goto Cleanup;

Fail:
component.Rollback();

Cleanup:
Marshal.ReleaseComComponent(component);
Marshal.ReleaseComComponent(server);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Did you notice any duplication in there? The examples sport a comment that will stay in there forever, but I found it amusing to add a goto statement. Most would likely have bundled a bunch of nested ifs?
We could maybe throw in each if, and have an outer try...&lt;br /&gt;
The awake reader will also notice that the first two ifs for the component's common &lt;code&gt;SetupData&lt;/code&gt; and &lt;code&gt;BindData&lt;/code&gt; methods test for other values than zero.&lt;br /&gt;
Said API is like that, and the reason for this is still a mystery to me. One day I might get to know.
Note that both &lt;code&gt;Server&lt;/code&gt; and &lt;code&gt;ComponentA&lt;/code&gt; are interfaces. They don't follow the .net convention of prefixing an &lt;code&gt;I&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then finally of course we have to do this fancy static call to release each of the unmanaged instances.&lt;/p&gt;
&lt;p&gt;For orders sake, I intentionally wrote the example in C# 1.0 style to drive the nail in.&lt;/p&gt;
&lt;h2&gt;How should it be?&lt;/h2&gt;
&lt;p&gt;In the .NET world we're used to things throwing exceptions. Things we have to clean up are all &amp;quot;disposable&amp;quot;.
Wouldn't it be nice if the code above was as such too? (To save some bytes and indenting we're now doing C# 8.0)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using var server = ComponentFactory.CreateServer();
using var component = server.CreateComponent&amp;lt;IComponentA&amp;gt;();
try
{
    server.LogOn();

    component.Initialize();
    component.DoFirstThing();
    component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
    component.Rollback();
    SomeLogger.LogError(ex);
    throw;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm feeling confident I don't have to argue the benefit of the latter  example.
So how do we make the API work like this instead? Hopefully not by wrapping absolutely all the components
with hand crafted code. Not generated classes either, if all they contain are the duplicated four lines of code per call.&lt;br /&gt;
Enter Castle.DynamicProxy!&lt;/p&gt;
&lt;h2&gt;Dynamic proxies&lt;/h2&gt;
&lt;p&gt;If you've ever used ORMs like NHibernate and Entity Framework, mocking libraries like Moq and RhinoMocks,
webservice proxies and whatnot, you've probably been using them without even knowing. Dependency Injection Frameworks (Especially Castle Windsor) may also provide fancy mechanisms to decorate without adding more implementations.&lt;br /&gt;
Dynamic Proxies are a mechanism to generate code at runtime. If you have a non-sealed class, you can have a derived class built at runtime, and then instantiate that one.&lt;/p&gt;
&lt;p&gt;The wonderful thing about them is that they can be used to wrap existing implementations. (not only yors)
Take the example of the return code in the ancient API. Wouldn't it be nice if all the methods did that
zero-test automagically, and then threw an exception if they failed?&lt;/p&gt;
&lt;h2&gt;Creating proxies&lt;/h2&gt;
&lt;p&gt;A first requirement is to take control over the instantiation. In the example we're using the
&lt;code&gt;Server&lt;/code&gt; instance is the factory for all components. All of those are already proxies,
although they are &amp;quot;made&amp;quot; using Microsofts proprietary .NET Framework interop technology and DCOM. (Proprietary Windows distributed transaction handling)
That makes the system able to handle transactional business logic on a central server.
We have no control over &lt;em&gt;those&lt;/em&gt; proxies, though, so we have to wrap and swap out that &lt;code&gt;Components[ComponentTypesEnum type]&lt;/code&gt; indexer with something we control.
We want to use Castle DynamicProxy to add an &lt;code&gt;IInterceptor&lt;/code&gt; that can intercept calls to the component.&lt;/p&gt;
&lt;p&gt;In my solution I came up with this extension that just makes it look like we have a generic overload on &lt;code&gt;Server&lt;/code&gt;. One could investigate ways of preventing calls to the &amp;quot;old&amp;quot; one if convension is not enough.&lt;/p&gt;
&lt;p&gt;Code to create a &amp;quot;component&amp;quot; becomes &lt;code&gt;server.CreateComponent&amp;lt;T&amp;gt;(ComponentTypeEnum type)&lt;/code&gt;, which seems a bit more &amp;quot;factoryish&amp;quot;. We still have the enum in there, but I'll leave my suggested fix for later. &lt;em&gt;(Writing the blogpost, I cringe at the missing opportunity to remove it...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To be able to add an interceptor to all calls to a type we use a &lt;code&gt;ProxyGenerator&lt;/code&gt; instance, and call it's &lt;code&gt;CreateInterfaceProxyWithTarget&lt;/code&gt; method. To which we pass the component itself, and one or more &lt;code&gt;IInterceptor&lt;/code&gt; implementations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class ComponentFactory
{
    private static readonly ProxyGenerator Generator = new ProxyGenerator();

    public static T Create&amp;lt;T&amp;gt;(this Server server, ComponentTypeEnum componentType)
    {
        var component = server.Components[(int)componentType];
        return Decorate&amp;lt;T&amp;gt;(component);
    }

    public static T Decorate&amp;lt;T&amp;gt;(object component)
    {
        var proxy = (T)Generator.CreateInterfaceProxyWithTarget(
            component,
            new ErrorWhenResultNotZeroInterceptor()
        );
        return proxy;
    }

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see we still use the &lt;code&gt;Components[index]&lt;/code&gt; &amp;quot;factory&amp;quot; indexer to create the requested component, but we use Castle DynamicProxy to add our &lt;code&gt;ErrorWhenResultNotZeroInterceptor&lt;/code&gt;. &lt;/p&gt;
&lt;h2&gt;The decorating error handler&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;IInterceptor&lt;/code&gt; implementation requires one single method to be implemented: &lt;code&gt;Intercept(IInvocation invocation)&lt;/code&gt;. It will be called for &lt;em&gt;any&lt;/em&gt; extendable or implementable member on the decorated instance.
In order for the decorated instance to be called at all, we must call &lt;code&gt;invocation.Proceed()&lt;/code&gt;.
Otherwise, we've basically create a &amp;quot;no-op&amp;quot; override.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;IInvocation&lt;/code&gt; instance contains information about the member intercepted, the return value after invocation etc. We're certainly interested in the return value.&lt;/p&gt;
&lt;p&gt;But remember we have those two rogue methods that don't return zero for success? That's where the reflection info comes in handy. Here's the entire code for the &amp;quot;convert non-zero to exception&amp;quot; mechanism:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ErrorWhenResultNotZeroInterceptor : IInterceptor
{
    private static readonly Dictionary&amp;lt;string, int&amp;gt; okValues = new Dictionary&amp;lt;string, int&amp;gt;
    {
        { &amp;quot;SetupData&amp;quot;, 1 },
        { &amp;quot;BindData&amp;quot;, -1 }
    };

    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
        var result = (int)invocation.ReturnValue;
        var methodName = invocation.Method.Name;
        var expectedSuccess = okValues.ContainsKey(methodName) ? okValues[methodName] : 0;
        if (result != expectedSuccess)
        {
            throw new CustomAPIException(result, methodName);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Et voilá! Now any time we call a component's methods and it doesn't return a zero, an exception is thrown instead.&lt;/p&gt;
&lt;p&gt;We've handled the biggest annoyance, and can now write code as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try
{
    server.LogOn();

    component.SetupData();
    component.BindData();
    component.DoFirstThing();
    component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
    component.Rollback();
    throw;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;As you might notice, my ideal code had &lt;code&gt;component.Initialize()&lt;/code&gt; instead of the &lt;code&gt;Setup/Bind&lt;/code&gt; pair. I leave it as an excercise for the reader to create that extension.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Disposability&lt;/h2&gt;
&lt;p&gt;To intercept calls to &lt;code&gt;IDisposable.Dispose&lt;/code&gt; and replace them with calls to &lt;code&gt;Marshal.ReleaseComObject&lt;/code&gt; requires a fairly simple &lt;code&gt;IInterceptor&lt;/code&gt; implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class DisposableInterceptor : IInterceptor
{
    private const int MaxReleases = 100;
    private readonly object real;
    private readonly Guid id = Guid.NewGuid();

    public DisposableInterceptor(object real)
    {
        this.real = real;
    }

    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method.Name != &amp;quot;Dispose&amp;quot;) return;

        var i = 0;
        var result = -1;
        while (result != 0 &amp;amp;&amp;amp; i++ &amp;lt; MaxReleases)
        {
            result = Marshal.ReleaseComObject(real);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But the whole endeavour is about to get quite a bit more painful. There might be easier ways, but I actually ended up generating some code pre-compile time for this trick. However, there's only &lt;em&gt;one&lt;/em&gt; line of code per component in the API. (Two, really, but hey...)&lt;/p&gt;
&lt;p&gt;Thing is that neither the core &lt;code&gt;Component&lt;/code&gt; interface, nor the derived &lt;code&gt;IndividualComponent&lt;/code&gt; interfaces implement the &lt;code&gt;IDisposable&lt;/code&gt; interface. So we have to create our own &lt;code&gt;IDisposableIndividualComponent&lt;/code&gt; interfaces that inherit both the individual one and &lt;code&gt;IDisposable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For me the most natural step was to create an explicitly ran unit test I can run at will. It's really a one off, but the tests were great for getting there:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
[Explicit(&amp;quot;Run this to generate pastable code with disposable versions of API components&amp;quot;)]
public class CodeGeneration
{
    [Test]
    public void GenerateDisposableCustomComponents()
    {
        var types = GetComponentTypes();
        foreach (var type in types)
        {
            Console.WriteLine($&amp;quot;public interface I{type.Name} : {type.Name}, IDisposable {{}}&amp;quot;);
        }
    }

    [Test]
    public void GenerateTypeMapForFactory()
    {
        var types = GetComponentTypes();
        foreach (var type in types)
        {
            Console.WriteLine($&amp;quot;{{ typeof(I{type.Name}), typeof({type.Name}) }},&amp;quot;);
        }
    }

    private static IOrderedEnumerable&amp;lt;Type&amp;gt; GetComponentTypes()
    {
        var baseType = typeof(IComponent);
        var allTypes = baseType.Assembly.GetModules().SelectMany(m =&amp;gt; ModuleTypes(m));
        var types = allTypes
            .Where(t =&amp;gt; t.IsInterface &amp;amp;&amp;amp; baseType.IsAssignableFrom(t) &amp;amp;&amp;amp; !t.Name.StartsWith(&amp;quot;I&amp;quot;))
            .OrderBy(t =&amp;gt; t.Name);
        return types;
    }

    private static Type[] ModuleTypes(Module m)
    {
        try
        {
            return m.GetTypes();
        }
        catch (ReflectionTypeLoadException e)
        {
            return e.Types.Where(t =&amp;gt; t != null).ToArray();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first test generates one line per component as follows. We're somewhat in luck as well. The API in question doesn't follow the .net convension of prefixing interface names with &lt;code&gt;I&lt;/code&gt;, so we don't have to invent any ingenious pre- or suffix to our interface names.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IServer : Server, IDisposable { }
public interface IComponentB : ComponentB, IDisposable { }
public interface IComponentC : ComponentC, IDisposable { }
// ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;(Yes, the API sports better names that ComponentA, B etc. I'm just obfuscating it.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The second test generates a map for the factory to find the right interface to decorate the actual one with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private static readonly Dictionary&amp;lt;Type, Type&amp;gt; TypeMap = new()
{
    { typeof(IServer), typeof(Server) },
    { typeof(IComponentB), typeof(ComponentB) },
    { typeof(IComponentC), typeof(ComponentC) },
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Likely both groups of &amp;quot;one-liners&amp;quot; per component could've been generated at runtime as well, but I'm sure this'll save some CPU cycles.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is the place to suggest that the enum parameter could also get its own map and we could ditch that last factory argument.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The factory can now be updated to create instances of our disposable interfaces instead of the original interfaces. Castle DynamicProxy's &lt;code&gt;ProxyGenerator&lt;/code&gt; has another factory method called &lt;code&gt;CreateInterfaceProxyWithTargetInterface&lt;/code&gt;. It takes a bunch more arguments than the one we've used till now. It needs the new disposable type, the original implementation type, the actual component and the decorators. &lt;/p&gt;
&lt;p&gt;Additionally, we need to discriminate between calls to the &lt;code&gt;Dispose&lt;/code&gt; method, calls to methods returning &lt;code&gt;int&lt;/code&gt; and other methods when we intercept. We don't want either interceptor to run on incompatible methods.&lt;/p&gt;
&lt;p&gt;The factory method also accepts an options instance where we can pass something called an &lt;code&gt;IInterceptorSelector&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's the complete factory code looking up the types and sewing it all together:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class ComponentFactory
{
    private static readonly ProxyGenerator Generator = new ProxyGenerator();

    private static readonly ProxyGenerationOptions Options = new ProxyGenerationOptions
    {
        Selector = new InterceptorSelector()
    };

    public static T Create&amp;lt;T&amp;gt;(this Server server, ComponentTypeEnum componentType)
    {
        var component = server.Components[(int)componentType];
        return DecorateWithDisposable&amp;lt;T&amp;gt;(component);
    }

    public static T DecorateWithDisposable&amp;lt;T&amp;gt;(object component)
    {
        var targetType = TypeMap.ContainsKey(typeof(T)) ? TypeMap[typeof(T)] : typeof(T);
        var implemented = TypeMap.ContainsKey(typeof(T))
            ? new[] { typeof(IDisposable), typeof(T) }
            : new[] { typeof(IDisposable) };
        var proxy = (T)Generator.CreateInterfaceProxyWithTargetInterface(
            targetType,
            implemented,
            component,
            Options,
            new DisposableInterceptor(component),
            new ErrorWhenResultNotZeroInterceptor()
        );
        return proxy;
    }

    public static Server CreateServer()
    {
        return DecorateWithDisposable&amp;lt;Server&amp;gt;(new ServerClass());
    }

    private static readonly Dictionary&amp;lt;Type, Type&amp;gt; TypeMap = new() {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The interceptor selector then does some &amp;quot;simple&amp;quot; filtering (and a few nice modern C# pattern matches) to select the relevant interceptor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class InterceptorSelector : IInterceptorSelector
{
    public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
    {
        if (method.ReturnType != typeof(int))
            interceptors = interceptors.Where(x =&amp;gt; x is not ErrorWhenResultNotZeroInterceptor).ToArray();

        if (method.DeclaringType == typeof(IDisposable))
            return interceptors;

        return interceptors.Where(x =&amp;gt; x is not DisposableInterceptor).ToArray();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that's it! We can now write code like such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using var server = ComponentFactory.CreateServer();
using var component = server.CreateComponent&amp;lt;IComponentA&amp;gt;();
try
{
    server.LogOn();

    component.Initialize();
    component.DoFirstThing();
    component.DoSecoondThing();
}
catch(CustomAPIException ex)
{
    component.Rollback();
    SomeLogger.LogError(ex);
    throw;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;So we've gone on a deep dive through both &amp;quot;easy&amp;quot; extensions and more complex ones like adding disposability. I really hope it sparked some inspiration.&lt;/p&gt;
&lt;p&gt;I'm sure there are tons of ways to utilize this, but make sure to use it as the right tool for the right job, and not for the sake of using it.&lt;/p&gt;
&lt;p&gt;For one, the decorator pattern is way easier to apply to large interfaces than generating tons of wrapper code to decorate only a few of the methods on a type. I usually write individual classes for each use case (read command or query), so generic decorators can be as simple as &lt;code&gt;IInterceptor&lt;/code&gt; implementations anyway.
But for APIs not built like this, Castle DynamicProxy makes it a breeze!&lt;/p&gt;
&lt;p&gt;For debugging purposes, turning on and off a console logger that prints each method called and its result is also quite nice. Especially within unit-tests.&lt;/p&gt;
&lt;p&gt;Got any other great ideas? Drop them in the comments.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Aug 2022 21:04:43 Z</pubDate>
      <a10:updated>2022-08-30T21:04:43Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1204</guid>
      <link>https://blog.aabech.no/archive/analyzing-w3c-logs-with-excel-and-powerquery/</link>
      <category>powerquery</category>
      <title>Analyzing W3C logs with Excel and PowerQuery</title>
      <description>&lt;p&gt;15 Years ago I wrote &lt;a href="https://sourceforge.net/projects/iislogviewer/"&gt;a .net WinForms tool called &amp;quot;IIS Log Viewer&amp;quot;&lt;/a&gt; to parse and visualize W3C log files in a nice color coded grid. Of course it topples over as soon as you analyze a file of some respectable size, so I'm sorry to say I abandoned maintenance of the program. Only today I realize I should have called it &amp;quot;W3C Log Viewer&amp;quot;, but here's to hindsight. 🤣🍻&lt;/p&gt;
&lt;p&gt;Today I use a different approach with Excel and PowerQuery instead. In this article I'll show how you can anlyze a fair amount of traffic (at least a million requests) from several logfiles possibly spanning your entire retention period. I'm a Microsoft fanboy, so I'll show how to do it with Azure App Services logging to Azure Storage accounts, but &lt;a href="https://support.microsoft.com/en-us/office/combine-files-in-a-folder-with-combine-binaries-power-query-94b8023c-2e66-4f6b-8c78-6a00041c90e4"&gt;you can point to filesystem folders as well&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setting up W3C logging on an Azure App Service&lt;/h2&gt;
&lt;p&gt;The nicest way of HTTP logging I know of on Azure is by logging to storage. It'll persist across slots when you swap, and you won't have to download them to analyze them.&lt;/p&gt;
&lt;p&gt;You'll find the settings you want under &lt;code&gt;Monitoring\App Service Logs&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1065/appservice-storage-logging.png" alt="Web Server logging set to storage with retention period" /&gt;&lt;/p&gt;
&lt;p&gt;Now, you'll production slot will happily rotate n days of logs in your storage account. They'll be stored in a hierarchy by slot name (?), year, month, date and finally hour. Totally horrible to analyze without a tool, but we got just the one! Also too difficult to screenshot, so you'll have to imagine it. 😇&lt;/p&gt;
&lt;h2&gt;Getting data into Excel&lt;/h2&gt;
&lt;p&gt;I believe PowerQuery really made it's break into Excel in the 2016 version. We'd had some &amp;quot;Power&amp;quot; tools (pivot?) from 2013, but this version really hit the target! I can't get over the fact I waited until 2020 to get more acquainted with it.&lt;/p&gt;
&lt;p&gt;In the old versions, we more or less only had the horrible text import / split column wizard. In newer versions of Excel we've got a super rich &amp;quot;Get &amp;amp; Transform Data&amp;quot; section in the Data ribbon, not to speak of (almost) the full power of PowerQuery behind the scenes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1046/excel-data-bar.png" alt="The Excel data bar with new options" /&gt;&lt;br /&gt;
&lt;em&gt;The &amp;quot;new&amp;quot; Excel data ribbon&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I particularly like the &amp;quot;From Web&amp;quot; one, since it'll handle most modern formats such as JSON and XML. (Even CSV)&lt;/p&gt;
&lt;p&gt;However, for our purposes, we'll find our source of choice by drilling into the &amp;quot;Get Data&amp;quot; dropdown:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1066/blob-storage-source.png" alt="Excel Get Data From Azure Blob Storage menu" /&gt;&lt;/p&gt;
&lt;p&gt;We go on to enter our storage account name:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1045/entering-storage-account.png" alt="Entering storage account name" /&gt;&lt;/p&gt;
&lt;p&gt;Then you'll need to &lt;a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal#view-account-access-keys"&gt;find your account key&lt;/a&gt; and enter it in order to authorize with Azure:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1044/entering-account-key.png" alt="Entering storage account key" /&gt;&lt;/p&gt;
&lt;p&gt;Mind the &lt;a href="https://docs.microsoft.com/en-us/power-query/dataprivacyfirewall"&gt;security concerns and the data firewall&lt;/a&gt; when authenticating sources and sharing connected Excel files.&lt;/p&gt;
&lt;p&gt;The next &amp;quot;wizard&amp;quot; we're faced with is a blob storage browser. We'll select the &amp;quot;logs&amp;quot; container, but leave it at that. We want to &amp;quot;load and &lt;em&gt;transform&lt;/em&gt;&amp;quot; all the blobs we see on the right hand side.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1050/navigating-to-all-logs.png" alt="Viewing the logs container in the PowerQuery navigator" /&gt;&lt;/p&gt;
&lt;p&gt;So the final step is to resist the urge to click &amp;quot;Load&amp;quot;, but rather click &amp;quot;Transform Data&amp;quot;. This will launch the most powerful data transformation tool I know to date, the aptly named &amp;quot;PowerQuery&amp;quot; GUI.&lt;/p&gt;
&lt;h2&gt;Getting the contents of all the log files&lt;/h2&gt;
&lt;p&gt;Excel will immediately pop a new (sadly modal) window with a new Query aptly called &amp;quot;logs&amp;quot;. The smiley up there is for sending feedback, but I like to think PowerQuery is just a happy place alltogether.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1060/viewing-blobs-in-powerquery.png" alt="The PowerQuery GUI while viewing log blobs in PowerQuery" /&gt;&lt;/p&gt;
&lt;p&gt;As you can see there's a bunch of options up top, but we're actually after a slightly more inconspicuous one down in the &amp;quot;Content&amp;quot; column header called &amp;quot;Combine files&amp;quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1042/combine-files-button.png" alt="The inconspicuous combine files button circled" /&gt;&lt;/p&gt;
&lt;p&gt;This will pop another dialog asking us about encoding and whether we want to skip things that bugs out. Also we can choose which file to use as our sample for further transformation:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1043/combine-files-dialog.png" alt="The combine files dialog" /&gt;&lt;/p&gt;
&lt;p&gt;We'll go ahead and use the first one, since the sequence of those don't matter. The're all in the same format. What follows is this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1059/viewing-blob-content.png" alt="Blob content now visible in the query results" /&gt;&lt;/p&gt;
&lt;p&gt;We're now faced with a fairly more complex set of queries and steps in our &amp;quot;logs&amp;quot; query. The inner workings of those are out of the scope of this post, but I promise there's magic to be had if you'll dive deeper. The .net peeps reading this will also appreciate the familiar form of static method calling in the &amp;quot;formula bar&amp;quot;. PowerQuery is a delightful blend of familiar syntaxes. 😇&lt;/p&gt;
&lt;p&gt;The more interesting thing however is that (very quickly) the blob file contents are shown in a new column called &amp;quot;Column1&amp;quot;. This is our next target.&lt;/p&gt;
&lt;h2&gt;Transforming the data and preparing for analysis&lt;/h2&gt;
&lt;p&gt;Before we can make anything meaningful out of the actual log data we have to get rid of some junk, and figure out what fields we've logged. These are the first two lines of the logfile. Alas they've got a bit of junk attached. The first line is just garbage, so we'll employ our first secret weapon. Remove top rows: (it's on the Home ribbon)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1052/remove-top-rows.png" alt="The remove top rows menu item" /&gt;&lt;/p&gt;
&lt;p&gt;You'll be prompted with a dialog asking how many rows to remove. We want to remove the leading hash lines except the &amp;quot;#Fields&amp;quot; one. In my case, this is just the &amp;quot;#Software&amp;quot; row, so I remove 1 row.&lt;/p&gt;
&lt;p&gt;The next step is to remove the leading &amp;quot;#Fields: &amp;quot; text of the column header specification. We'll drag out our second weapon of choice, &amp;quot;Replace Values&amp;quot;. For this step we need to select &amp;quot;Column1&amp;quot; and move over to the &amp;quot;Transform&amp;quot; ribbon. Here we'll find the &amp;quot;Replace Values&amp;quot; dropdown where we select the values option:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1055/replace-values-menu.png" alt="GUI when replacing values" /&gt;&lt;/p&gt;
&lt;p&gt;We're faced with (yet) another dialog in which we specify that we want to get rid of the &amp;quot;#Fields: &amp;quot; string: (make sure to include the space)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1054/replace-values-dialog.png" alt="The replace values dialog" /&gt;&lt;/p&gt;
&lt;h2&gt;Splitting columns&lt;/h2&gt;
&lt;p&gt;We're here! The good old &amp;quot;split column&amp;quot; phase is up!&lt;/p&gt;
&lt;p&gt;Back at the home ribbon, we find our old friend &amp;quot;Split Column&amp;quot;. For W3C log files we're dealing with space delimited files, so we go with the &amp;quot;by delimiter&amp;quot; option:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1056/split-column-by-delimiter.png" alt="The split column menu" /&gt;&lt;/p&gt;
&lt;p&gt;Yet another dialog where we specify space as the delimiter:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1057/split-column-dialog.png" alt="The split column by delimiter dialog" /&gt;&lt;/p&gt;
&lt;p&gt;Now we're definitely getting somewhere:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1063/after-split-columns.png" alt="First view with actual split log columns" /&gt;&lt;/p&gt;
&lt;p&gt;Notice how we didn't have to specify any other options than how to split the data? No data types, no headers, nothing.&lt;/p&gt;
&lt;p&gt;We do need to get those headers as column names, though. We're already on the home ribbon and if you've been looking closely you've likely noticed both &amp;quot;Remove Columns&amp;quot; and &amp;quot;Use First Row as Headers&amp;quot;. We'll remove the now useless &amp;quot;Source.Name&amp;quot; column, and then use the first row as our headers:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1067/remove-source-column.png" alt="Removing the source column" /&gt;&lt;/p&gt;
&lt;p&gt;Then we hit the &amp;quot;Use First Row as Headers&amp;quot; button, et voilá:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1062/after-promote-headers.png" alt="Log files properly parsed" /&gt;&lt;/p&gt;
&lt;p&gt;The utter sweetness (if you look in the bottom right corner) is that PowerQuery is awesome at guessing data types. Granted you have to nudge it a bit every now and then, but the date, times and numeric data is already properly typed.&lt;/p&gt;
&lt;p&gt;Which leads us to the last necessary step for this to work flawlessly. PowerQuery has only sampled 1000 rows from the first file. It's not aware that several more lines starting with a hash is awaiting further down the road. That's gonna mess up it's date conversion for the first column. Here's for our final trick, &amp;quot;Remove errors&amp;quot;. It's an option under our old friend &amp;quot;Remove rows&amp;quot; on the home ribbon. Make sure to select the date column before you hit it:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1053/removing-hash-row-errors.png" alt="Removing hash row errors" /&gt;&lt;/p&gt;
&lt;p&gt;And that should be it! We're ready to hit that captivating &amp;quot;Close &amp;amp; Load&amp;quot; button up in the left corner of the home bar. Go ahead! Hit it!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1041/close-and-load.png" alt="The close and load button" /&gt;&lt;/p&gt;
&lt;p&gt;Now depending on whether you've got less than a mil. lines total in your log files this will succeed or fail. Despair not though, there are ways to get around it. I'll add some tips at the bottom.&lt;/p&gt;
&lt;p&gt;If you're lucky enough to escape the Excel row limit trap (unlike some unfortunate brits recently), you'll be faced with a fancy green table after a while. The &amp;quot;Queries &amp;amp; Connections&amp;quot; sidebar will appear and show progress of how many rows that's been loaded. (Including errors if any)&lt;br /&gt;
For me it takes a few minutes to load half a mil rows. (Granted I'm on a 1GB internet connection yielding .5 GB to my box. It is a few hundred MB to download.)&lt;br /&gt;
Here's how it looks:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1048/log-files-in-sheet.png" alt="W3C log files loaded to an Excel sheet" /&gt;&lt;/p&gt;
&lt;p&gt;So we've got half a million log rows loaded to Excel. I can't be bothered to investigate the 680 errors, but if you click the link you'll get a special query showing those and their causes. (❤)&lt;/p&gt;
&lt;p&gt;However this is fairly hard to get some value out of in its present state. We need to pivot. If you've got less than a million you can go to the &amp;quot;Insert&amp;quot; ribbon and click &amp;quot;Pivot table&amp;quot;. Make sure you've selected a cell in the log table first. Just accept the default options and skip to the next section.&lt;/p&gt;
&lt;h2&gt;Pivoting more than a million rows&lt;/h2&gt;
&lt;p&gt;Incidentally pivoting is also the trick (untested) if you've got more than a million rows. You'll have to right click the &amp;quot;logs&amp;quot; query in the &amp;quot;Queries &amp;amp; Connections&amp;quot; toolbar on the right and select &amp;quot;Load to&amp;quot;. You can then change to a &amp;quot;Pivot Table&amp;quot; instead of table and skip loading all the details. I believe there's also options to build cached data models, but that's outside of my skillset and the scope of this post as well. :)&lt;/p&gt;
&lt;h2&gt;Pivoting W3C logs&lt;/h2&gt;
&lt;p&gt;Which ever way you opt to pivot the log data you'll end up with a worksheet like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1040/clean-pivot-view.png" alt="A fresh pivot table in Excel" /&gt;&lt;/p&gt;
&lt;p&gt;I've got two favorite setups for analyzing logs. First of all, I'd like to examine whether a spike is due to a new bot of some sorts. We'll just block it if it's something we're not interested in. It can save an amazing amount of processing you possibly weren't aware you spent. &lt;/p&gt;
&lt;p&gt;Though we imported all the log files, for this you'd likely want to look at a specific day. Dragging &amp;quot;date&amp;quot; to &amp;quot;Filters&amp;quot; lets you select a date in B2. (Full screenshot further down)&lt;/p&gt;
&lt;p&gt;Next we want to look at agents, so we drag &amp;quot;cs(User-Agent)&amp;quot; to &amp;quot;Rows&amp;quot;. (They'll all appear instantly)&lt;/p&gt;
&lt;p&gt;Finally we want to see how many hits we've got from each agent and then sort. We can actually drag any field, but let's go with &amp;quot;time&amp;quot; for counting. The final dragging step is to drag &amp;quot;time&amp;quot; into &amp;quot;Values&amp;quot;.&lt;/p&gt;
&lt;p&gt;We should be left with something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1058/unsorted-pivot.png" alt="Unsorted list of web agent hits" /&gt;&lt;/p&gt;
&lt;p&gt;As you can see we still have quite a few agents to try to rid ourselves of, but the ones we see here have too acceptable request rates to bother. The one's we'd be interested in would have thousands in the &amp;quot;Count of time&amp;quot; column. To find them easily we have to sort, and this is were Excel can be a bit quirky. In order to sort the pivot by &amp;quot;Count of time&amp;quot; we have to open the dropdown menu on &amp;quot;Row Labels&amp;quot; and select &amp;quot;More Sort Options...&amp;quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1049/more-sort-options-menu.png" alt="The more sort options menu for Excel pivot sorting" /&gt;&lt;/p&gt;
&lt;p&gt;What follows is a rather quirky dialog, but we're after &amp;quot;Descending (Z to A) by: Count of time&amp;quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1047/excel-pivot-sort-dialog.png" alt="Excel Pivot sort dialog" /&gt;&lt;/p&gt;
&lt;p&gt;We're now left with a table showing that todays winner was (bloody) iPhones!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1061/w3c-pivot-sorted-by-agent.png" alt="Aggregated W3C logs sorted by agent hit count" /&gt;&lt;/p&gt;
&lt;h2&gt;Discovering facts&lt;/h2&gt;
&lt;p&gt;If we'd had an excessive agent, likely with &amp;quot;bot&amp;quot; in it's name, the next step would be to add &amp;quot;c-ip&amp;quot; as a secondary row dimension under &amp;quot;cs(User-Agent)&amp;quot;. We could then drill down into the excessive agent and likely discover IPs to block or even report.&lt;/p&gt;
&lt;p&gt;Today however, this process lead me to replacing &amp;quot;cs(User-Agent)&amp;quot; as my rows field with &amp;quot;cs-uri-stem&amp;quot;. Repeating the sorting trick showed me that the customer obviously had had a campaign of some sorts leading to the sudden spike at three o'clock. Luckily our newly upgraded S3 plan managed to take it only dropping to 2 secs response time across 29 Umbraco sites, but I'm about to have a serious talk with this particular customer - leading to a slightly more expensive hosting fee for an individual plan. 😇 &lt;/p&gt;
&lt;p&gt;Another favourite setup of mine shows which URIs (or other interesting dimensions) have errors, redirects or excessive &amp;quot;access denieds&amp;quot;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Rows
&lt;ul&gt;
&lt;li&gt;cs-uri-stem&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Columns
&lt;ul&gt;
&lt;li&gt;sc-status&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Values
&lt;ul&gt;
&lt;li&gt;time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using custom tools for file based logs seems to be bygones. PowerQuery can do most popular serialization formats, and within Excel limits it's awesome combined with pivot tables. If the performance isn't good enough, the same techniques can be used with SQL Server, Analysis Services and Power BI, but I guess W3C log format parsing is unnecessary in those kinds of setups.  
&lt;/p&gt;
&lt;p&gt;I find myself using PowerQuery for more and more these days, so don't be surprised if another PowerQuery post sees the light of day this year. Hope you enjoyed following along and maybe even learned something. 🤓&lt;/p&gt;
</description>
      <pubDate>Wed, 06 Jan 2021 00:34:42 Z</pubDate>
      <a10:updated>2021-01-06T00:34:42Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1174</guid>
      <link>https://blog.aabech.no/archive/stubbing-the-internet-with-postman-httpclient-and-httpmessagehandler/</link>
      <category>unit testing</category>
      <title>Stubbing the internet with Postman, HttpClient and HttpMessageHandler</title>
      <description>&lt;h2&gt;Using Postman or Insomnia to test code and integrations&lt;/h2&gt;
&lt;p&gt;Recently &lt;a href="https://twitter.com/timheuer?s=20"&gt;Tim Heuer&lt;/a&gt; at Microsoft posted a poll on Twitter
asking people using tools like Postman and Insomnia why they &amp;quot;just don't rely on test frameworks&amp;quot;.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Folks who use tools like postman/insomnia...why don&amp;#39;t you just rely on test frameworks?&lt;/p&gt;&amp;mdash; Tim Heuer (@timheuer) &lt;a href="https://twitter.com/timheuer/status/1266177093664006145?ref_src=twsrc%5Etfw"&gt;May 29, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt; &lt;/p&gt;
&lt;p&gt;I wanted to pitch in with the method I use, which is a combination of both.&lt;/p&gt;
&lt;p&gt;Whenever I integrate with third parties, I almost always start off with &lt;a href="https://en.wikipedia.org/wiki/Spike_(software_development)"&gt;a spike&lt;/a&gt; to learn how their APIs work.&lt;br /&gt;
Right now for instance, I'm building some integrations with the &lt;a href="https://brreg.no"&gt;norwegian business registry&lt;/a&gt;.&lt;br /&gt;
They have a couple of nice and simple endpoints. Here's a Postman request searching for our company, &lt;a href="https://www.markedspartner.no"&gt;MarkedsPartner&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1038/brreg-name-search.png" alt="Searching for MarkedsPartner with Brreg APIs" /&gt;&lt;/p&gt;
&lt;p&gt;I also had to run this code from a public webserver in order to find the right request messages from a second third party, so I threw in some &lt;code&gt;HttpClient&lt;/code&gt; usage for the above, while logging incoming requests.&lt;/p&gt;
&lt;p&gt;I could then replicate the requests in Postman to continue spiking with my mediating API controller.&lt;/p&gt;
&lt;p&gt;As soon as I knew all about the incoming and outgoing messages, I was ready to start stabilizing with tests so I could proceed with refactoring and extending.&lt;/p&gt;
&lt;h2&gt;Stubbing the entire internet&lt;/h2&gt;
&lt;p&gt;The .NET &lt;code&gt;HttpClient&lt;/code&gt; is fully stubbable, but the &lt;a href="https://www.informit.com/articles/article.aspx?p=359417&amp;amp;seqNum=2"&gt;seam&lt;/a&gt; is fairly well hidden.
I'm not convinced enough people know about this.&lt;br /&gt;
The &lt;code&gt;HttpClient&lt;/code&gt; has a parameterized constructor taking the abstract &lt;code&gt;HttpMessageHandler&lt;/code&gt; as its only parameter. By default this is a concrete &lt;code&gt;HttpClientHandler&lt;/code&gt; when the parameterless ctor is used.
Within &lt;code&gt;HttpMessageHandler&lt;/code&gt; there is a protected &lt;code&gt;SendAsync&lt;/code&gt; method which is the final method called by all the &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;Put&lt;/code&gt;, &lt;code&gt;Post&lt;/code&gt; methods etc.
It receives a &lt;code&gt;HttpRequestMessage&lt;/code&gt; and returns a &lt;code&gt;HttpResponseMessage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can use mocking frameworks like &lt;code&gt;Moq&lt;/code&gt; to stub protected methods. But I really like to build my own fake &lt;code&gt;HttpMessageHandler&lt;/code&gt; implementation for HTTP integration tests. In my experience it's actually (way) less code, and provides both stubbing and mocking mechanisms.&lt;/p&gt;
&lt;p&gt;The simplest way to build it is to just return the same message for any call, but with a few extensions it's almost like a small stub internet with static resources. Here's how you could build one that varies its responses by request URI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class StubHttpClientHandler : HttpClientHandler
{
    public readonly Dictionary&amp;lt;string, HttpResponseMessage&amp;gt; UriResponses = new Dictionary&amp;lt;string, HttpResponseMessage&amp;gt;();

    protected override async Task&amp;lt;HttpResponseMessage&amp;gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var url = request.RequestUri.ToString();
        if (UriResponses.ContainsKey(url))
        {
            return await Task.FromResult(UriResponses[url]);
        }

        return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To make sure my production call hits this integration border instead of going off to the internet, I have to abstract away the creation of a &lt;code&gt;HttpClient&lt;/code&gt; in my code. Often I set up a &lt;code&gt;Func&amp;lt;HttpClient&amp;gt;&lt;/code&gt; factory with a DI container, but in my current case it's enough to add a static property to the SUT (system under test):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static Func&amp;lt;HttpClient&amp;gt; HttpClientFactory = () =&amp;gt; new HttpClient();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In my tests, I then swap out that factory with a function that returns a reference to a &lt;code&gt;HttpClient&lt;/code&gt; using my &lt;code&gt;StubHttpClientHandler&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var handler = new StubHttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
BrregLookupController.HttpClientFactory = () =&amp;gt; httpClient;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The final thing to do is to register a URI with a corresponding response message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handler.UriResponses.Add(
    &amp;quot;https://data.brreg.no/enhetsregisteret/api/enheter?navn=markedspartner&amp;quot;,
    new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new StringContent(&amp;quot;{\&amp;quot;status\&amp;quot;:\&amp;quot;OK\&amp;quot;}&amp;quot;, Encoding.UTF8)
    }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whenever the controller now fires a call to that URL, it'll get a response with the JSON provided in the response message. You could add a content-type header and such for good measure, but it's not really necessary depending on how you handle the result.&lt;/p&gt;
&lt;h2&gt;Bringing Postman responses into the tests&lt;/h2&gt;
&lt;p&gt;Of course a slim JSON object with a status message isn't what I'm after here. I'm after the exact response the Brreg APIs would give me.
Instead of pasting the entire JSON graph seen in the screenshot above, I like to keep them in files instead.&lt;/p&gt;
&lt;p&gt;Those kinds of files are excellent candidates for Embedded Resources in the test project. So I paste the entire graph into a JSON file in a &amp;quot;TestResources&amp;quot; folder and make sure the file's build action is &amp;quot;Embedded Resource&amp;quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1039/response-as-resource.png" alt="Example resource file in Visual Studio" /&gt;&lt;/p&gt;
&lt;p&gt;In order to easily provide that content to my stub I usually employ a simple helper class for resources:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class TestData
{
    public static string ReadAsString(string resourceName, string suffix = &amp;quot;json&amp;quot;)
    {
        using (var resourceStream = typeof(TestData).Assembly.GetManifestResourceStream(
            $&amp;quot;Full.TestProject.Namespace.TestResources.{resourceName}.{suffix}&amp;quot;))
        {
            if (resourceStream == null)
            {
                throw new InvalidOperationException($&amp;quot;Couldn't find resource '{resourceName}'&amp;quot;);
            }
            return new StreamReader(resourceStream, Encoding.UTF8).ReadToEnd();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can change the stub response as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handler.UriResponses.Add(
    &amp;quot;https://data.brreg.no/enhetsregisteret/api/enheter?navn=markedspartner&amp;quot;,
    new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new StringContent(TestData.ReadAsString(&amp;quot;NameSearchResult.MarkedsPartner&amp;quot;), Encoding.UTF8)
    }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Verifying the integration&lt;/h2&gt;
&lt;p&gt;As long as the SUT returns something serializable, in this case it's an API controller returning a strongly typed transformed version of the data from Brreg, we can verify the entire graph using &lt;a href="https://approvaltests.com"&gt;ApprovalTests&lt;/a&gt;. I've &lt;a href="http://blog.aabech.no/archive/exploiting-approvaltests-for-clearer-tests/"&gt;blogged about approvaltests before&lt;/a&gt; if you don't know what it is.
Here's the entire act / assert part of the test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var response = await controller.Lookup(new LookupRequest
{
    Name = &amp;quot;MarkedsPartner&amp;quot;
});

Approvals.VerifyJson(JsonConvert.SerializeObject(response, JsonConfigurationAttribute.SerializerSettings));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I want to make sure I serialize exactly as the controller formatter will, so I pass the production serialization settings to ApprovalTests. On a sidenote, &lt;code&gt;Approvals.VerifyJson&lt;/code&gt; works best with non-indented JSON, so make sure to use settings that have formatting set to None.&lt;/p&gt;
&lt;h2&gt;Expanding on the stubbed world wide web&lt;/h2&gt;
&lt;p&gt;There are a couple of other mechanisms I enjoy to use with the &lt;code&gt;StubHttpClientHandler&lt;/code&gt;. First of all, we can use it as a mock to verify that our code sends out one-way messages as well. In order to do that we simply record every request in the &lt;code&gt;SendAsync&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class StubHttpClientHandler : HttpClientHandler
{
    public readonly List&amp;lt;HttpRequestMessage&amp;gt; Requests = new List&amp;lt;HttpRequestMessage&amp;gt;();
    public readonly Dictionary&amp;lt;string, HttpResponseMessage&amp;gt; UriResponses = new Dictionary&amp;lt;string, HttpResponseMessage&amp;gt;();

    protected override async Task&amp;lt;HttpResponseMessage&amp;gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Requests.Add(request);

        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can then assert that URLs and even bodies were sent off by our code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Assert.That(handler.Requests, Has.One
    .Property(&amp;quot;RequestUri&amp;quot;)
    .EqualTo(new Uri(&amp;quot;https://data.brreg.no/enhetsregisteret/api/enheter?navn=MarkedsPartner&amp;quot;))
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second mechanism I enjoy is to provide predicates and response factories in order to simplify setup.&lt;br /&gt;
For my current code I haven't refined it very much, so it might look a bit complex, but I'm sure you can follow the code. 😊&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class StubHttpClientHandler : HttpClientHandler
{
    public readonly List&amp;lt;HttpRequestMessage&amp;gt; Requests = new List&amp;lt;HttpRequestMessage&amp;gt;();
    public readonly Dictionary&amp;lt;string, HttpResponseMessage&amp;gt; UriResponses = new Dictionary&amp;lt;string, HttpResponseMessage&amp;gt;();
    public readonly List&amp;lt;(Func&amp;lt;HttpRequestMessage, bool&amp;gt; predicate, Func&amp;lt;HttpRequestMessage, HttpResponseMessage&amp;gt; responseFactory)&amp;gt; PredicateResponses = 
        new List&amp;lt;(Func&amp;lt;HttpRequestMessage, bool&amp;gt; predicate, Func&amp;lt;HttpRequestMessage, HttpResponseMessage&amp;gt; responseFactory)&amp;gt;();

    protected override async Task&amp;lt;HttpResponseMessage&amp;gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Requests.Add(request);

        // Return any match for full URIs
        var url = request.RequestUri.ToString();
        if (UriResponses.ContainsKey(url))
        {
            return await Task.FromResult(UriResponses[url]);
        }

        // Run all predicates and see if one matches
        var predicateEntry = PredicateResponses.FirstOrDefault(x =&amp;gt; x.predicate(request));
        if (predicateEntry.responseFactory != null)
        {
            // Create the response using the provided factory
            return await Task.FromResult(predicateEntry.responseFactory(request));
        }

        return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can set up a list of different responses as a dictionary, and then provide different responses based on the parameter. Here we could also add checks that authorization headers or other request requirements are verified by our production code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, Func&amp;lt;string&amp;gt;&amp;gt; searchResults = new Dictionary&amp;lt;string, Func&amp;lt;string&amp;gt;&amp;gt;
{
    { &amp;quot;MarkedsPartner&amp;quot;, () =&amp;gt; TestData.ReadAsString(&amp;quot;NameSearchResult.MarkedsPartner&amp;quot;)}
};

handler.PredicateResponses.Add((
    predicate: request =&amp;gt; 
        request.RequestUri.ToString().StartsWith(&amp;quot;https://data.brreg.no/enhetsregisteret/api/enheter?navn=&amp;quot;),
    responseFactory: request =&amp;gt;
        searchResults.ContainsKey(request.RequestUri.ParseQueryString()[&amp;quot;navn&amp;quot;])
            ? new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(searchResults[request.RequestUri.ParseQueryString()[&amp;quot;navn&amp;quot;]](), Encoding.UTF8)
            }
            : new HttpResponseMessage(HttpStatusCode.NotFound)
));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now hide away the slightly unrefactored and ugly stubbing code, while focusing on the parameter / response pairs in the &lt;code&gt;searchResults&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;p&gt;A search for &amp;quot;MarkedsPartner&amp;quot; will return the expected response, while a search for &amp;quot;Umbraco&amp;quot; for instance would return a 404 response.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using Postman or Insomnia is really useful for spiking, and it's invaluable when we need stub data for our tests. By leveraging files and embedded resources, we can easily build strong sets of integration tests, stubbed at the very frontier before the code runs off to the internet.&lt;/p&gt;
&lt;p&gt;Making sure your own API controllers behave correctly is a matter of verifying with your production binding and serialization settings. Then you won't have to call your own code via tools such as Postman or Insomnia.&lt;/p&gt;
&lt;p&gt;This is all necessary for .NET Framework users. For light .NET Core solutions, it should be fairly simple to spin up an inproc webserver and do the entire HTTP process in a speedy manner. However, the stubbed version of the internet is gold here as well.&lt;/p&gt;
&lt;p&gt;Hope you found the code interesting and thanks for reading!&lt;/p&gt;
&lt;p&gt;Here's a copy of the complete &lt;code&gt;StubHttpClientHandler&lt;/code&gt; described in this post:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class StubHttpClientHandler : HttpClientHandler
{
    public readonly List&amp;lt;HttpRequestMessage&amp;gt; Requests = new List&amp;lt;HttpRequestMessage&amp;gt;();
    public readonly Dictionary&amp;lt;string, HttpResponseMessage&amp;gt; UriResponses = new Dictionary&amp;lt;string, HttpResponseMessage&amp;gt;();
    public readonly List&amp;lt;(Func&amp;lt;HttpRequestMessage, bool&amp;gt; predicate, Func&amp;lt;HttpRequestMessage, HttpResponseMessage&amp;gt; responseFactory)&amp;gt; PredicateResponses = 
        new List&amp;lt;(Func&amp;lt;HttpRequestMessage, bool&amp;gt; predicate, Func&amp;lt;HttpRequestMessage, HttpResponseMessage&amp;gt; responseFactory)&amp;gt;();

    protected override async Task&amp;lt;HttpResponseMessage&amp;gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Requests.Add(request);

        var url = request.RequestUri.ToString();
        if (UriResponses.ContainsKey(url))
        {
            return await Task.FromResult(UriResponses[url]);
        }

        var predicateEntry = PredicateResponses.FirstOrDefault(x =&amp;gt; x.predicate(request));
        if (predicateEntry.responseFactory != null)
        {
            return await Task.FromResult(predicateEntry.responseFactory(request));
        }

        return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
    }
}
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Fri, 05 Jun 2020 10:52:32 Z</pubDate>
      <a10:updated>2020-06-05T10:52:32Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1167</guid>
      <link>https://blog.aabech.no/archive/search-and-rss-for-our-umbraco-contentlist/</link>
      <category>umbraco</category>
      <title>Search and RSS for our Umbraco ContentList</title>
      <description>&lt;p&gt;The ContentList package have gotten a few new improvements over the last couple of weeks. Here's a rundown on what's new since the &lt;a href="//blog.aabech.no/archive/our-umbraco-community-contentlist-10-beta-released/"&gt;initial release post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Theme control&lt;/h2&gt;
&lt;p&gt;Often a theme is customized to work well with just one or a few of the data sources. This is especially relevant with the changes described later in this post. Also, a theme might not use bootstrap, and as such not have the same rigid column count structure. Themes have therefore got a new config file you can choose to add to a theme folder: &lt;code&gt;list.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are two options you can add to the list config file:&lt;/p&gt;
&lt;h3&gt;Compatible sources&lt;/h3&gt;
&lt;p&gt;To specify which data sources a theme goes well with, add a &lt;code&gt;compatibleSources&lt;/code&gt; array:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;compatibleSources&amp;quot;: [
        &amp;quot;ListablesByLuceneDataSource&amp;quot;,
        &amp;quot;ListableByNodeDataSource&amp;quot;,
        &amp;quot;ListableChildrenDataSource&amp;quot;
    ] 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Disable columns setting&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/lars-erik/Our.Umbraco.ContentList/tree/master/Our.Umbraco.ContentList.Web/Views/Partials/ContentList/FlexTheme"&gt;FlexTheme&lt;/a&gt; example view for instance does its own flexbox styling with varying amounts of columns. For this theme, the columns setting with &amp;quot;mobile&amp;quot;, &amp;quot;tablet&amp;quot; and &amp;quot;desktop&amp;quot; column counts make no sense. &lt;/p&gt;
&lt;p&gt;You can now disable the columns setting as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;disableColumnsSetting&amp;quot;: true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Search&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ListableByLuceneDataSource&lt;/code&gt; have gotten a small brushup. The query setting have gotten its textbox expanded for better UX, and a new parameter called &amp;quot;Fulltext Querystring Parameter&amp;quot; have been added.&lt;/p&gt;
&lt;p&gt;The latter will combine the &amp;quot;query&amp;quot; with a search in all fields for all terms in a phrase extracted from the querystring parameter specified in the setting.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1036/contentlist-search-datasource-settings.png" alt="New parameters in the lucene data source" /&gt;&lt;/p&gt;
&lt;p&gt;There is a &lt;a href="https://github.com/lars-erik/Our.Umbraco.ContentList/tree/master/Our.Umbraco.ContentList.Web/Views/Partials/ContentList/SearchResult"&gt;new sample template for search&lt;/a&gt; where a search box is included in the theme list view. It posts and controls the configured querystring parameter. You can of course also just make a form in your layout posting to a given page with a content list. Or even &lt;a href="https://github.com/lars-erik/Our.Umbraco.ContentList/blob/38a65b9d7f43fec69340ffa31192bd759869fb86/Our.Umbraco.ContentList.Web/Config/grid.editors.config.js#L51"&gt;a grid editor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1037/contentlist-search-output.png" alt="Example output of search data source" /&gt;&lt;/p&gt;
&lt;h2&gt;Rss&lt;/h2&gt;
&lt;p&gt;I didn't want to bundle RSS with the core component, so there's a new package on the block called &lt;code&gt;Our.Umbraco.ContentList.Rss&lt;/code&gt;. By installing it, you get another data source aptly called &amp;quot;Rss&amp;quot;. It's also currently in beta, so remember to add the &lt;code&gt;-pre&lt;/code&gt; option when installing.&lt;/p&gt;
&lt;p&gt;It has one parameter for the URL to the RSS-feed, so I gather it doesn't need that much documentation. &lt;/p&gt;
&lt;p&gt;By specifying some theme settings as described earlier, it should be fairly simple to control both RSS and search themes combined with whatever other creative lists you come up with.&lt;/p&gt;
&lt;p&gt;Hope y'all enjoy and find this stuff useful! Don't be shy, come over to &lt;a href="https://github.com/lars-erik/Our.Umbraco.ContentList"&gt;the github repo&lt;/a&gt; and post issues if you want to discuss, ask questions or (please do) contribute.&lt;/p&gt;
</description>
      <pubDate>Wed, 06 May 2020 13:30:33 Z</pubDate>
      <a10:updated>2020-05-06T13:30:33Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1165</guid>
      <link>https://blog.aabech.no/archive/ourumbracowysiwyggrid-100-beta-released/</link>
      <category>umbraco</category>
      <title>Our.Umbraco.WysiwygGrid 1.0.0 beta released</title>
      <description>&lt;p&gt;A while ago I &lt;a href="/archive/the-state-of-wysiwyg-grid-editing-in-umbraco-a-prototype/"&gt;blogged about the state of WYSIWYG in the Umbraco grid editor&lt;/a&gt;. We're now about to add this script to a second and likely third Umbraco 8 site, so I had to generalize and package up the code.&lt;/p&gt;
&lt;p&gt;There's nothing much to hide or earn from here, so I decided to put it up on github and nuget instead of our internal scm and feeds.&lt;/p&gt;
&lt;p&gt;Hope to see some more community members fiddling with it, if nothing else that it inspires the block editor team to make the backoffice experience more similar to the frontend look and feel.&lt;/p&gt;
&lt;p&gt;A definite change from the previous blogpost is that we favor grid settings over inline styling. By slightly modifying the bootstrap view (we use v4 anyway), we can add multiple classes instead of attributes as is done the original views. As such we can have settings like these:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1035/wysiwyg-grid-settings.png" alt="Example grid settings dialog" /&gt;&lt;/p&gt;
&lt;p&gt;Those will generate extra classes named &lt;code&gt;padding-lots&lt;/code&gt; and &lt;code&gt;theme-inverse&lt;/code&gt;.&lt;br /&gt;
Those classes are added to rows or cells in both the backoffice and the front-end.&lt;br /&gt;
By doing some fancy sass include ninjaing, we can hopefully limit the amount of duplication, while still mimicing the front-end as much as possible in the backoffice.&lt;/p&gt;
&lt;p&gt;There are some samples in the github repo if you're unsure how to get started. The SQLCE database is included with a fairly common test username/password. 😇&lt;/p&gt;
&lt;p&gt;Without further ado, here's the nuget and github links:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lars-erik/Our.Umbraco.WysiwygGrid"&gt;https://github.com/lars-erik/Our.Umbraco.WysiwygGrid&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://www.nuget.org/packages/Our.Umbraco.WysiwygGrid/1.0.0-beta01"&gt;https://www.nuget.org/packages/Our.Umbraco.WysiwygGrid/1.0.0-beta01&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hope you enjoy it, and as usual, hope for a good discussion or feedback from the community!&lt;/p&gt;
</description>
      <pubDate>Thu, 23 Apr 2020 12:09:36 Z</pubDate>
      <a10:updated>2020-04-23T12:09:36Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1164</guid>
      <link>https://blog.aabech.no/archive/the-state-of-wysiwyg-grid-editing-in-umbraco-a-prototype/</link>
      <category>umbraco</category>
      <title>The state of WYSIWYG grid editing in Umbraco - a prototype</title>
      <description>&lt;h2&gt;The state of the grid&lt;/h2&gt;
&lt;p&gt;Since the grid was launched at the CG talk &amp;quot;The sky is the limit&amp;quot; back in 2015 (i think),
it has been a joy to use for editors, designers and developers alike.
Granted, there's been the other feature that could've gotten more love (configuration and settings), but generally it does the job swell.&lt;br /&gt;
However, it's still just a white canvas with &amp;quot;settings applied&amp;quot;.&lt;br /&gt;
&lt;a href="https://twitter.com/aaantoinee"&gt;Antoine&lt;/a&gt; made &lt;a href="https://our.umbraco.com/packages/backoffice-extensions/lepainter/"&gt;LePainter&lt;/a&gt; back in 2015, but likely didn't get much traction.&lt;br /&gt;
I was just told about it today by &lt;a href="https://twitter.com/callumbwhyte"&gt;Callum&lt;/a&gt;. Shame on me for not watching the package space enough, how embarrasing. 😳&lt;/p&gt;
&lt;p&gt;Anyway...&lt;/p&gt;
&lt;h2&gt;Why should we care&lt;/h2&gt;
&lt;p&gt;Recently, I've been in lots of discussions; near battles IMO; about which CMS to sell to our customers.&lt;br /&gt;
The feedback is that editors wonder why what they see while editing does not match what they see in the front-end.
We do work with another CMS where the backoffice experience has a near 1:1 layout and design with the front-end.
As developers, we tend to think structured data is the better solution. Think nested- and stacked content.
I tend to agree. But in a sales meeting, those arguments are total losing arguments. A competing agency will bury you when they oversell the fancy editing experience. Even though the not-so-apparent technical features of the competing CMS are totally useless.&lt;br /&gt;
And I do agree - the editing experience is what Umbraco likes to sell as well!&lt;/p&gt;
&lt;h2&gt;What can we do?&lt;/h2&gt;
&lt;p&gt;The current HQ efforts are working on the &amp;quot;block based&amp;quot; editor. It's been going on for the better part of a year, but the publicly available information is mostly &lt;a href="https://github.com/umbraco/rfcs/blob/master/cms/0012-rfc-block%20editor.md"&gt;just an RFC with screenshots&lt;/a&gt; looking like stacked / nested content, and that's it. The technical aspects seem super, but there's nothing much (but text) about the general editing experience. Also, and I know this is a hot take, editors DO want to see three columns in the editor when it ends up three columns in the front-end.&lt;/p&gt;
&lt;p&gt;Hopefully, as mentioned in the RFC, it can act as a stepping stone towards the &amp;quot;Grid 2.0&amp;quot; (which we started work on at the retreat &lt;em&gt;two&lt;/em&gt; years ago).&lt;/p&gt;
&lt;p&gt;But as far as I'm concerned, this could just as well end up never happening. The effort is not very visible, Project UniCore 🦄 has (rightly) high priority, and in the mean time we're stuck with Grid 1.0, or maybe, hopefully, a rumored &lt;em&gt;commercial&lt;/em&gt; package called Bento by the eminent &lt;a href="https://twitter.com/pgregorynz"&gt;Peter Gregory&lt;/a&gt; and his colleagues at KØBEN Digital.&lt;/p&gt;
&lt;h2&gt;A prototype&lt;/h2&gt;
&lt;p&gt;So back using Grid 1.0 for projects to be delivered imminently, I've started doing some prototypes for something similar to what LePainter did for early v7 projects.&lt;/p&gt;
&lt;p&gt;I asked the community if it was madness or worth pursuing, and the general feedback is that it's both. Hence this blog post to explore it some more, and hopefully gather some more feedback or at least provide feedback to the block editor team. (Hope they receive it. 😁)&lt;/p&gt;
&lt;p&gt;Here's a quick little preview of what I've been doing so far:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/B6LLiO116jc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2&gt;The issues&lt;/h2&gt;
&lt;p&gt;Actually getting the styling into the grid is &amp;quot;just&amp;quot; a matter of including a few pagefuls of JavaScript and a limited and scoped CSS file in a package.manifest file for the site. Both classes and styles are applied as expected, and with some due dilligence it doesn't look half bad.&lt;/p&gt;
&lt;p&gt;The biggest issue I have is that the cells and controls are styled white, and while styling one have to mind not messing up the GUI text like &amp;quot;Settings applied&amp;quot;.&lt;/p&gt;
&lt;p&gt;I attempted to lessen the opacity of the cells and controls, and it works to a degree. The minute one adds a background image however, the entire thing starts to crumble. How to keep the WYSIWYG experience while still seeing the GUI? One could fantasize about doing an average color sample of the image and toggling between white and black text, maybe? Could be done in a short hacking sprint...&lt;br /&gt;
Could just keep it fairly white like I've done in the prototype.
Could keep just the active rows white. This is where I start struggling. 😳&lt;/p&gt;
&lt;p&gt;To be frank, the biggest problem lies in the fact that the &amp;quot;Add content&amp;quot; button is &lt;em&gt;in&lt;/em&gt; the grid. Other CMSes have drag drop functionality from a menu over to the side. It could possibly still just be white or slightly transparent. The last problem then is all those &amp;quot;settings applied&amp;quot; and empty cells with the other type of &amp;quot;Add content&amp;quot; button.&lt;/p&gt;
&lt;p&gt;I can't help but think this is all within grasp, albeit just out of my limited back-ender reach. 😆&lt;/p&gt;
&lt;h2&gt;The settings dialog issues&lt;/h2&gt;
&lt;p&gt;The settings dialog is a chapter of itself. It's a mystery to me why we still have a concept of individual &amp;quot;prevalue editors&amp;quot; in Umbraco. Property editors have the same model, and can be sneaked in there by just providing a full path.&lt;/p&gt;
&lt;p&gt;However, as experienced when working on &lt;a href="http://blog.aabech.no/archive/our-umbraco-community-contentlist-10-beta-released/"&gt;our ContentList package&lt;/a&gt;, nesting property editors using built-in directives wreaks total havok on the styling. There's some grey background and drop shadows - that I haven't seen being actively used anywhere. It's generally just a mystery, and a huge candidate for backoffice re-work.&lt;/p&gt;
&lt;p&gt;Also, for instance the slider property editor does look a bit weird when used as a setting editor. I do hope that those things will get an overhaul and are made more uniform sooner or later.&lt;/p&gt;
&lt;p&gt;I'll just leave that hanging, since I know (and appreciate) there are several HQ and community efforts going on to clean things up.&lt;/p&gt;
&lt;h2&gt;The code&lt;/h2&gt;
&lt;p&gt;Since I managed to &lt;a href="https://github.com/umbraco/Umbraco-CMS/pull/2639/commits/76c924fbaa3da89d9865d9ec405e6dbb123852e3"&gt;sneak in a few new events in the grid&lt;/a&gt;, making it work was a matter of hooking into the &amp;quot;grid.initialized&amp;quot; event and start watching the model. There might be some performance issues with large grids since it's a deep watch, but with todays computers that might be irrational over engineering to think as well.&lt;/p&gt;
&lt;p&gt;It adds the configured styles as expected.&lt;br /&gt;
For the configuration, it just adds the value of the configuration setting called &amp;quot;class&amp;quot;. If there's more it concats the setting key with the setting value, delimited by a dash.&lt;/p&gt;
&lt;p&gt;You can scrutinize the JavaScript here, or in &lt;a href="https://gist.github.com/lars-erik/9dbf8f4004121c8eb109423b814af34c"&gt;this gist&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;angular.module(&amp;quot;umbraco&amp;quot;).run([&amp;quot;eventsService&amp;quot;, function(eventsService) {
    function findModelScope(scope) {
        if (!scope) {
            return null;
        }
        if (scope.model) {
            return scope;
        }
        return findModelScope(scope.$parent);
    }

    function isProtectedClass(className) {
        return className === &amp;quot;umb-row-inner&amp;quot; ||
            className === &amp;quot;umb-cell-inner&amp;quot; ||
            className.substr(0, 2) === &amp;quot;ng-&amp;quot; ||
            className.substr(0, 2) === &amp;quot;ui-&amp;quot;;
    }

    function addClasses(element, gridItem) {
        function classNameFromConfig(e) {
            if (e === &amp;quot;class&amp;quot;) {
                return gridItem.config[e];
            } else {
                return e + &amp;quot;-&amp;quot; + gridItem.config[e];
            }
        }

        var classes = (element.className || &amp;quot;&amp;quot;).split(/\s+/);
        var newClasses = classes.filter(isProtectedClass);
        var nameClass = (gridItem.name || &amp;quot;&amp;quot;).toLowerCase().replace(&amp;quot; &amp;quot;, &amp;quot;-&amp;quot;);
        var configClasses = Object.keys(gridItem.config || {}).map(classNameFromConfig);
        newClasses.push(nameClass);
        newClasses = newClasses.concat(configClasses);
        element.className = newClasses.join(&amp;quot; &amp;quot;);
    }

    function addStyles(element, gridItem) {
        function styleFromKeyPair(e) {
            return e + &amp;quot;:&amp;quot; + gridItem.styles[e];
        }

        var stylesValues = Object.keys(gridItem.styles || {}).map(styleFromKeyPair);
        element.style = stylesValues.join(&amp;quot;;&amp;quot;);
    }

    eventsService.on(&amp;quot;grid.initialized&amp;quot;,
        function(evt, data) {
            var modelScope = findModelScope(data.scope);
            var model = modelScope.model;
            var jqEl = data.element;
            var el = data.element.get(0);
            jqEl.addClass(&amp;quot;stylized-grid&amp;quot;);

            modelScope.$watch(
                &amp;quot;model&amp;quot;,
                function () {
                    var areaElements = el.getElementsByClassName(&amp;quot;umb-column&amp;quot;);
                    if (areaElements.length === 0) {
                        return;
                    }
                    model.value.sections.forEach(function (area, ai) {
                        var rowElements = areaElements[ai].getElementsByClassName(&amp;quot;umb-row-inner&amp;quot;);
                        area.rows.forEach(function (row, ri) {
                            var rowElement = rowElements[ri];
                            addClasses(rowElement, row);
                            addStyles(rowElement, row);

                            var cellElements = rowElement.getElementsByClassName(&amp;quot;umb-cell-inner&amp;quot;);
                            row.areas.forEach(function(cell, ci) {
                                addClasses(cellElements[ci], cell);
                                addStyles(cellElements[ci], cell);
                            });
                        });
                    });
                },
                true
            );
        });

}]);
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The CSS&lt;/h2&gt;
&lt;p&gt;To not interfere with any other backoffice styling, the script adds the class &amp;quot;stylized-grid&amp;quot; to the grid container. Here's the (S)CSS I used to make things more transparent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.umb-grid.stylized-grid { 

    .umb-cell-content {
        background-color: rgba(255, 255, 255, .8);
    }

    .umb-cell-content:hover {
        background-color: rgba(255, 255, 255, 1);
    }

    .umb-cell-content.-has-editors {
        background-color: rgba(255, 255, 255, .8);
    }

    .umb-cell-content.-has-editors:hover {
        background-color: rgba(255, 255, 255, 1);
    }

    .umb-grid-add-more-content {
        background-color: rgba(255, 255, 255, 1);
    }

    .umb-control-bar, .umb-grid-has-config {
        font-family: Lato, Helvetica Neue, Helvetica, Arial, sans-serif !important;
    }

    iframe {
        background-color: transparent;
    }

    @import &amp;quot;../../../Assets/grid&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The import at the bottom is the sass that styles our individual components in the front-end, and that should be reflected in the backoffice. &lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We'll be diving into deep water and testing this out on a real customer. I'm a bit weary, but we need to get the experience.&lt;/p&gt;
&lt;p&gt;I do hope I've been able to provide some inspiration, and that WYSIWYG editing in Umbraco might get the renaissance (I believe) it deserves.&lt;/p&gt;
&lt;p&gt;Feel free to drop your comments below or ping me on Twitter or Slack!&lt;br /&gt;
Also happy to continue the discussion on our or github, but not sure the best way forward, if any.&lt;/p&gt;
</description>
      <pubDate>Fri, 03 Apr 2020 13:26:14 Z</pubDate>
      <a10:updated>2020-04-03T13:26:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1163</guid>
      <link>https://blog.aabech.no/archive/our-umbraco-community-contentlist-10-beta-released/</link>
      <category>Umbraco</category>
      <title>Our Umbraco Community ContentList 1.0 (beta) released</title>
      <description>&lt;p&gt;ContentList is a grid editor we've used internally at MarkedsPartner since 2016. It helps editors and designers insert lists of content in the grid without having to worry about queries, paging or building new templates.&lt;br /&gt;
I've finally been able to upgrade it to Umbraco 8 and polished it a bit. It's still slightly in beta, but there's nothing special but a bit of CSS polish lacking, so it should be fairly safe to use in production. 👼&lt;/p&gt;
&lt;p&gt;There's a whole documentation piece in the readme &lt;a href="https://github.com/lars-erik/Our.Umbraco.ContentList"&gt;over on github&lt;/a&gt;, so I won't bother with a whole lot of prose in this blogpost, but I recorded a half an hour demo (below) showing all the aspects of implementing its use in an Umbraco site.&lt;/p&gt;
&lt;p&gt;There's really not much to do once you get the hang of it, and our editors and staff have been enjoying using it for five years, so I hope you will too.&lt;/p&gt;
&lt;p&gt;Here's the demo, and I really hope to get your feedback here, on github or on youtube.
The first 3-4 minutes show the gist of it, so no need to sit through the full half hour if you're just curious. 👍&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/7O6Es1SNf9s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;</description>
      <pubDate>Wed, 01 Apr 2020 21:57:27 Z</pubDate>
      <a10:updated>2020-04-01T21:57:27Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1162</guid>
      <link>https://blog.aabech.no/archive/armlinker-100-released/</link>
      <category>automation</category>
      <category>azure</category>
      <title>ARMLinker 1.0.0 released</title>
      <description>&lt;h2&gt;ARM What?&lt;/h2&gt;
&lt;p&gt;I've been having fun with ARM Templates the last couple of months.
It's a wonderful way to keep your Azure Resource definitions in source control.
Not to mention being able to parameterize deployment to different environments,
and not least keeping your secrets neatly tucked away in a vault.&lt;/p&gt;
&lt;p&gt;However, compiling a set of resources from multiple files currently requires
you to put your templates online. I want to keep most of our customer products'
templates private, and to do that one have to &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#linked-template"&gt;jump through hoops&lt;/a&gt; to copy the
files over to a storage account and link to the given URLs.
It kind of defeats the whole purpose for me.&lt;/p&gt;
&lt;p&gt;So I went and created a small tool to be able to link templates locally.&lt;/p&gt;
&lt;h2&gt;How to use it&lt;/h2&gt;
&lt;p&gt;There's an installable project type for Visual Studio called &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/create-visual-studio-deployment-project"&gt;&amp;quot;Azure Resource Group&amp;quot;&lt;/a&gt;.
When you create one you get a few files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy-AzureResourceGroup.ps1&lt;/li&gt;
&lt;li&gt;azuredeploy.json&lt;/li&gt;
&lt;li&gt;azuredeploy.parameters.json&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can stuff all of the resources you require in the azuredeploy.json file, and finally deploy them using a wizard, or run the PowerShell script in a CD pipeline.&lt;/p&gt;
&lt;p&gt;By installing ARMLinker you can start running the tool to link other JSON files
into the main azuredeploy.json file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;install-module ARMLinker
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's say we have a Logic App (what I've been doing).&lt;br /&gt;
To deploy it and its connections and other needed resources, we often want
a bounch of secret keys for different APIs and such.&lt;/p&gt;
&lt;p&gt;Here's a trimmed down sample of a Logic App that runs a SQL command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;$schema&amp;quot;: &amp;quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&amp;quot;,
    &amp;quot;contentVersion&amp;quot;: &amp;quot;1.0.0.0&amp;quot;,
    &amp;quot;parameters&amp;quot;: {
        &amp;quot;Tags&amp;quot;: {
            &amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,
            &amp;quot;defaultValue&amp;quot;: {
                &amp;quot;Customer&amp;quot;: &amp;quot;My customer&amp;quot;,
                &amp;quot;Product&amp;quot;: &amp;quot;Their Logic App&amp;quot;,
                &amp;quot;Environment&amp;quot;: &amp;quot;Beta&amp;quot;
            }
        },
        &amp;quot;SQL-Server&amp;quot;: {
            &amp;quot;defaultValue&amp;quot;: &amp;quot;some.database.windows.net&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;
        },
        &amp;quot;SQL-User&amp;quot;: {
            &amp;quot;defaultValue&amp;quot;: &amp;quot;appuser&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;
        },
        &amp;quot;SQL-Password&amp;quot;: {
            &amp;quot;defaultValue&amp;quot;: &amp;quot;&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;securestring&amp;quot;
        },
        &amp;quot;SQL-Database-Name&amp;quot;: {
            &amp;quot;defaultValue&amp;quot;: &amp;quot;beta-database&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;
        }
    },
    &amp;quot;variables&amp;quot;: {
        &amp;quot;ConnectionName&amp;quot;: &amp;quot;[replace(concat(parameters('Tags').Customer, '-', parameters('Tags').Product, '-SQLConnection-', parameters('Tags').Environment), ' ', '')]&amp;quot;,
        &amp;quot;LogicAppName&amp;quot;: &amp;quot;[replace(concat(parameters('Tags').Customer, '-', parameters('Tags').Product, '-', parameters('Tags').Environment), ' ', '')]&amp;quot;
    },
    &amp;quot;resources&amp;quot;: [
        {
            &amp;quot;type&amp;quot;: &amp;quot;Microsoft.Web/connections&amp;quot;,
            &amp;quot;apiVersion&amp;quot;: &amp;quot;2016-06-01&amp;quot;,
            &amp;quot;location&amp;quot;: &amp;quot;westeurope&amp;quot;,
            &amp;quot;name&amp;quot;: &amp;quot;[variables('ConnectionName')]&amp;quot;,
            &amp;quot;properties&amp;quot;: {
                &amp;quot;api&amp;quot;: {
                    &amp;quot;id&amp;quot;: &amp;quot;[concat(subscription().id,'/providers/Microsoft.Web/locations/westeurope/managedApis/sql')]&amp;quot;
                },
                &amp;quot;displayName&amp;quot;: &amp;quot;sql_connection&amp;quot;,
                &amp;quot;parameterValues&amp;quot;: {
                    &amp;quot;server&amp;quot;: &amp;quot;[parameters('SQL-Server')]&amp;quot;,
                    &amp;quot;database&amp;quot;: &amp;quot;[parameters('SQL-Database-Name')]&amp;quot;,
                    &amp;quot;authType&amp;quot;: &amp;quot;windows&amp;quot;,
                    &amp;quot;username&amp;quot;: &amp;quot;[parameters('SQL-User')]&amp;quot;,
                    &amp;quot;password&amp;quot;: &amp;quot;[parameters('SQL-Password')]&amp;quot;
                }
            }
        }, 
        {
            &amp;quot;type&amp;quot;: &amp;quot;Microsoft.Logic/workflows&amp;quot;,
            &amp;quot;apiVersion&amp;quot;: &amp;quot;2017-07-01&amp;quot;,
            &amp;quot;name&amp;quot;: &amp;quot;[variables('LogicAppName')]&amp;quot;,
            &amp;quot;dependsOn&amp;quot;: [ &amp;quot;[resourceId('Microsoft.Web/connections', variables('ConnectionName'))]&amp;quot; ], 
            &amp;quot;location&amp;quot;: &amp;quot;westeurope&amp;quot;,
            &amp;quot;properties&amp;quot;: {
                &amp;quot;state&amp;quot;: &amp;quot;Enabled&amp;quot;,
                &amp;quot;definition&amp;quot;: {
                    &amp;quot;$schema&amp;quot;: &amp;quot;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&amp;quot;,
                    &amp;quot;contentVersion&amp;quot;: &amp;quot;1.0.0.0&amp;quot;,
                    &amp;quot;parameters&amp;quot;: {
                        &amp;quot;$connections&amp;quot;: {
                            &amp;quot;defaultValue&amp;quot;: {},
                            &amp;quot;type&amp;quot;: &amp;quot;Object&amp;quot;
                        },
                        &amp;quot;SQL-Server&amp;quot;: {
                            &amp;quot;defaultValue&amp;quot;: &amp;quot;&amp;quot;,
                            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;
                        },
                        &amp;quot;SQL-Database-Name&amp;quot;: {
                            &amp;quot;defaultValue&amp;quot;: &amp;quot;&amp;quot;,
                            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot; 
                        }
                    },
                    &amp;quot;triggers&amp;quot;: {
                        &amp;quot;Recurrence&amp;quot;: {
                            &amp;quot;recurrence&amp;quot;: {
                                &amp;quot;frequency&amp;quot;: &amp;quot;Day&amp;quot;,
                                &amp;quot;interval&amp;quot;: 1
                            },
                            &amp;quot;type&amp;quot;: &amp;quot;Recurrence&amp;quot;
                        }
                    },
                    &amp;quot;actions&amp;quot;: {
                        &amp;quot;Execute_a_SQL_query_(V2)&amp;quot;: {
                            &amp;quot;runAfter&amp;quot;: {},
                            &amp;quot;type&amp;quot;: &amp;quot;ApiConnection&amp;quot;,
                            &amp;quot;inputs&amp;quot;: {
                                &amp;quot;body&amp;quot;: {
                                    &amp;quot;query&amp;quot;: &amp;quot;select 'do something really useful' as task&amp;quot;
                                },
                                &amp;quot;host&amp;quot;: {
                                    &amp;quot;connection&amp;quot;: {
                                        &amp;quot;name&amp;quot;: &amp;quot;@parameters('$connections')['sql']['connectionId']&amp;quot;
                                    }
                                },
                                &amp;quot;method&amp;quot;: &amp;quot;post&amp;quot;,
                                &amp;quot;path&amp;quot;: &amp;quot;/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('SQL-Server')))},@{encodeURIComponent(encodeURIComponent(parameters('SQL-Database-Name')))}/query/sql&amp;quot;
                            }
                        }
                    },
                    &amp;quot;outputs&amp;quot;: {}
                },
                &amp;quot;parameters&amp;quot;: {
                    &amp;quot;$connections&amp;quot;: {
                        &amp;quot;value&amp;quot;: {
                            &amp;quot;sql&amp;quot;: {
                                &amp;quot;connectionId&amp;quot;: &amp;quot;[resourceId('Microsoft.Web/connections', variables('ConnectionName'))]&amp;quot;,
                                &amp;quot;connectionName&amp;quot;: &amp;quot;variables('ConnectionName')&amp;quot;,
                                &amp;quot;id&amp;quot;: &amp;quot;/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Web/locations/westeurope/managedApis/sql&amp;quot;
                            }
                        }
                    },
                    &amp;quot;SQL-Server&amp;quot;: {
                        &amp;quot;value&amp;quot;: &amp;quot;[parameters('SQL-Server')]&amp;quot;
                    },
                    &amp;quot;SQL-Database-Name&amp;quot;: {
                        &amp;quot;value&amp;quot;: &amp;quot;[parameters('SQL-Database-Name')]&amp;quot;
                    }
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The parameters here are ARM template parameters. The most interesting one is the secret password for the database server. It's secret, so it's not supposed to live in our parameter file or source control. We've also got the ID of the connection, which will be the &lt;em&gt;real&lt;/em&gt; ID in the actual deployed Logic App.&lt;/p&gt;
&lt;p&gt;There's a fancy way to go about keeping the password in a key vault on Azure, and the Visual Studio Wizard is really helpful with putting it into a vault.&lt;/p&gt;
&lt;p&gt;When we're done and ready for production, a parameter file may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;$schema&amp;quot;: &amp;quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#&amp;quot;,
    &amp;quot;contentVersion&amp;quot;: &amp;quot;1.0.0.0&amp;quot;,
    &amp;quot;parameters&amp;quot;: {
        &amp;quot;Tags&amp;quot;: {
            &amp;quot;value&amp;quot;: {
                &amp;quot;Customer&amp;quot;: &amp;quot;My customer&amp;quot;,
                &amp;quot;Product&amp;quot;: &amp;quot;Their Logic App&amp;quot;,
                &amp;quot;Environment&amp;quot;: &amp;quot;Production&amp;quot;
            }
        },
        &amp;quot;SQL-Database-Name&amp;quot;: {
            &amp;quot;value&amp;quot;: &amp;quot;production-database&amp;quot;
        },
        &amp;quot;SQL-Password&amp;quot;: {
            &amp;quot;reference&amp;quot;: {
                &amp;quot;keyVault&amp;quot;: {
                    &amp;quot;id&amp;quot;: &amp;quot;/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/Vault-Group/providers/Microsoft.KeyVault/vaults/OurKeyVault&amp;quot;
                },
                &amp;quot;secretName&amp;quot;: &amp;quot;CustomerSQLPassword&amp;quot;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One of the beauties of using Logic Apps is that it have this nice GUI to work with in the portal. There's also an extension for Visual Studio to be able to edit them within Visual Studio.&lt;/p&gt;
&lt;p&gt;However, the definition will look like this when viewed with the code editor. (I removed the bulk of it, but notice the parameters) &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;definition&amp;quot;: {
        &amp;quot;$schema&amp;quot;: &amp;quot;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&amp;quot;,
        &amp;quot;actions&amp;quot;: {
            &amp;quot;Execute_a_SQL_query_(V2)&amp;quot;: {
                &amp;quot;inputs&amp;quot;: {
                    &amp;quot;body&amp;quot;: {
                        &amp;quot;query&amp;quot;: &amp;quot;select 'do something really useful' as task&amp;quot;
                    },
                    &amp;quot;host&amp;quot;: {
                        &amp;quot;...&amp;quot;
                    },
                    &amp;quot;...&amp;quot;
                },
                &amp;quot;runAfter&amp;quot;: {},
                &amp;quot;type&amp;quot;: &amp;quot;ApiConnection&amp;quot;
            }
        },
        &amp;quot;...&amp;quot;,
        &amp;quot;parameters&amp;quot;: {
            &amp;quot;$connections&amp;quot;: {
                &amp;quot;defaultValue&amp;quot;: {},
                &amp;quot;type&amp;quot;: &amp;quot;Object&amp;quot;
            },
            &amp;quot;...&amp;quot;
        },
        &amp;quot;triggers&amp;quot;: {
            &amp;quot;...&amp;quot;
        }
    },
    &amp;quot;parameters&amp;quot;: {
        &amp;quot;$connections&amp;quot;: {
            &amp;quot;value&amp;quot;: {
                &amp;quot;sql&amp;quot;: {
                    &amp;quot;connectionId&amp;quot;: &amp;quot;/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/CustomerResourceGroup/providers/Microsoft.Web/connections/MyCustomer-TheirProduct-SQLConnection-Prod&amp;quot;,
                    &amp;quot;connectionName&amp;quot;: &amp;quot;MyCustomer-TheirProduct-SQLConnection-Prod&amp;quot;,
                    &amp;quot;id&amp;quot;: &amp;quot;/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Web/locations/westeurope/managedApis/sql&amp;quot;
                }
            }
        },
        &amp;quot;SQL-Database-Name&amp;quot;: {
            &amp;quot;value&amp;quot;: &amp;quot;production-database&amp;quot;
        },
        &amp;quot;SQL-Server&amp;quot;: {
            &amp;quot;value&amp;quot;: &amp;quot;some.database.windows.net&amp;quot;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice that the parameters are all filled out. We can't copy this into our ARM template since it's all real Resource ID references.&lt;/p&gt;
&lt;p&gt;There's another way to get only the definition. We can use the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/az.logicapp"&gt;Az.LogicApp&lt;/a&gt; powershell module:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(get-azlogicapp -resourcegroupname CustomerResourceGroup -name mycustomer-theirproduct-prod).definition.ToString()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will give us only the &lt;code&gt;definition&lt;/code&gt; part of the template.&lt;/p&gt;
&lt;p&gt;Both gives us a means to put &lt;em&gt;only&lt;/em&gt; the &lt;em&gt;definition&lt;/em&gt; of the logic app into a file in our local project.&lt;/p&gt;
&lt;p&gt;Now we can go back to the ARM template and replace the definition with a simple link to the file.
Say we &lt;code&gt;Set-Content&lt;/code&gt; the result of the statement above into a file called &amp;quot;logicapp.json&amp;quot;. We can modify the ARM template as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;$schema&amp;quot;: &amp;quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&amp;quot;,
    &amp;quot;contentVersion&amp;quot;: &amp;quot;1.0.0.0&amp;quot;,
    &amp;quot;parameters&amp;quot;: {
        &amp;quot;...&amp;quot;
    },
    &amp;quot;variables&amp;quot;: {
        &amp;quot;...&amp;quot;
    },
    &amp;quot;resources&amp;quot;: [
        {
            &amp;quot;type&amp;quot;: &amp;quot;Microsoft.Web/connections&amp;quot;,
            &amp;quot;...&amp;quot;
        }, 
        {
            &amp;quot;type&amp;quot;: &amp;quot;Microsoft.Logic/workflows&amp;quot;,
            &amp;quot;apiVersion&amp;quot;: &amp;quot;2017-07-01&amp;quot;,
            &amp;quot;name&amp;quot;: &amp;quot;[variables('LogicAppName')]&amp;quot;,
            &amp;quot;dependsOn&amp;quot;: [ &amp;quot;[resourceId('Microsoft.Web/connections', variables('ConnectionName'))]&amp;quot; ], 
            &amp;quot;location&amp;quot;: &amp;quot;westeurope&amp;quot;,
            &amp;quot;properties&amp;quot;: {
                &amp;quot;state&amp;quot;: &amp;quot;Enabled&amp;quot;,
                &amp;quot;definition&amp;quot;: {
                    &amp;quot;templateLink&amp;quot; {
                        &amp;quot;uri&amp;quot;: &amp;quot;.\logicapp.json&amp;quot;
                    }
                },
                &amp;quot;parameters&amp;quot;: {
                    &amp;quot;$connections&amp;quot;: {
                        &amp;quot;value&amp;quot;: {
                            &amp;quot;sql&amp;quot;: {
                                &amp;quot;connectionId&amp;quot;: &amp;quot;[resourceId('Microsoft.Web/connections', variables('ConnectionName'))]&amp;quot;,
                            &amp;quot;connectionName&amp;quot;: &amp;quot;variables('ConnectionName')&amp;quot;,
                                &amp;quot;id&amp;quot;: &amp;quot;/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Web/locations/westeurope/managedApis/sql&amp;quot;
                            }
                        }
                    },
                    &amp;quot;...&amp;quot;
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By running &lt;code&gt;ARMLinker&lt;/code&gt; we will have the same generated file as we started with,
but we can use the GUI for the logic app and easily fetch the new JSON for it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Convert-TemplateLinks azuredeploy.json azuredeploy.linked.json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For now, I've actually turned those around and put the &amp;quot;linked&amp;quot; template in a file called azuredeploy.linked.json in order to generate the &amp;quot;conventional&amp;quot; azuredeploy.json file.&lt;/p&gt;
&lt;h2&gt;More options&lt;/h2&gt;
&lt;p&gt;When using the &amp;quot;copy content from the editor&amp;quot; method mentioned above, we have to make sure to copy &lt;em&gt;only&lt;/em&gt; the definition object. Otherwise we'll bring the concrete parameters into the local file.&lt;/p&gt;
&lt;p&gt;Do not despair!&lt;/p&gt;
&lt;p&gt;There's another option that doesn't match the official schema for &amp;quot;templateLink&amp;quot;.
By adding a property called &amp;quot;jsonPath&amp;quot; we can point to an object deeper in the linked file.
Say we copy the content from the online editor.&lt;/p&gt;
&lt;p&gt;We can modify the linked template as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;definition&amp;quot;: {
    &amp;quot;templateLink&amp;quot; {
        &amp;quot;uri&amp;quot;: &amp;quot;.\logicapp.json&amp;quot;,
        &amp;quot;jsonPath&amp;quot;: &amp;quot;definition&amp;quot;
    }
},
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will now only merge the definition part from the logicapp.json file.&lt;/p&gt;
&lt;p&gt;I only implemented dot separated paths for now, so exotic paths to arrays or paths with special characters won't work.&lt;/p&gt;
&lt;p&gt;IE. &lt;code&gt;resources[0]['very fancy'].thing&lt;/code&gt; won't work, but &lt;code&gt;things.with.dots&lt;/code&gt; will work.&lt;/p&gt;
&lt;h2&gt;Plans and dreams&lt;/h2&gt;
&lt;p&gt;This is pretty much only a workaround while waiting for Microsoft to realise this is totally useful and obvious.&lt;/p&gt;
&lt;p&gt;I originally intended it to be a Custom Tool for Visual Studio, but I could not for the life of me figure out how to enable Custom Tools in projects not of the C# or Visual Basic archetypes.&lt;/p&gt;
&lt;p&gt;If anyone picks up on it, I'll happily discuss new features and even happierly receive meaningful pull requests.&lt;/p&gt;
&lt;p&gt;Other than that, I believe it does the job properly. It can be used in CD pipelines. It should even work for any JSON, not necessarily ARM templates. &lt;/p&gt;
&lt;p&gt;I would really appreciate your feedback, and hope you like it!&lt;/p&gt;
&lt;p&gt;Now go commit and deploy something automagically while fetching coffee! 🤘😁🦄&lt;/p&gt;
&lt;h2&gt;Code and gallery links&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/lars-erik/ARMLinker"&gt;Github repository&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://www.powershellgallery.com/packages/ARMLinker/1.0.1"&gt;PowerShell gallery&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Wed, 22 Jan 2020 23:22:41 Z</pubDate>
      <a10:updated>2020-01-22T23:22:41Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1139</guid>
      <link>https://blog.aabech.no/archive/environmental-approvaltests/</link>
      <category>unit testing</category>
      <title>Environmental ApprovalTests</title>
      <description>&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;A while ago I wrote a &lt;a href="//blog.aabech.no/archive/exploiting-approvaltests-for-clearer-tests/"&gt;post about a tool called ApprovalTests&lt;/a&gt;. I've included it in my workshop on unit testing Umbraco, and people are amazed at its usefulness. Having Visual Studio pop a diff in your face when stuff breaks is a real timesaver. However, when I ran the workshop at CodeGarden 18, I realized people were concerned that their tests would be impossible to run in CI environments and the like. Not to worry - ApprovalTests have you covered. (Pun intended)&lt;/p&gt;
&lt;h2&gt;Environmentalism&lt;/h2&gt;
&lt;p&gt;When declaring reporters with ApprovalTests, you can specify multiple reporters. That's not all. The reporters have an extensive API on them, which caters for us tailoring everything. There is one particularly interesting interface on all &amp;quot;diff-reporters&amp;quot; named &lt;code&gt;IEnvironmentAwareReporter&lt;/code&gt;.&lt;br /&gt;
The &lt;code&gt;VisualStudioReporter&lt;/code&gt; one has a nice little one-liner implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public override bool IsWorkingInThisEnvironment(string forFile)
{
    return OsUtils.IsWindowsOs() &amp;amp;&amp;amp; base.IsWorkingInThisEnvironment(forFile) &amp;amp;&amp;amp; LaunchedFromVisualStudio();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The last function call there checks if the current process is a descendent of &lt;code&gt;devenv.exe&lt;/code&gt;. If it isn't, execution will just be passed to the next reporter in the chain. So there! It won't break your CI build.&lt;/p&gt;
&lt;p&gt;But what do we want in our CI build, then? Probably a regular WhateverUnit assertion. I use NUnit, so that'll be our example.&lt;/p&gt;
&lt;h2&gt;Falling back&lt;/h2&gt;
&lt;p&gt;Take this little test. I approved the result for seed 1, and we'll examine the output for an invalid result by swapping to 2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
[UseReporter(typeof(VisualStudioReporter))]
public class When_Running_In_Different_Environments
{
    [Test]
    public void Delegates_To_Most_Appropriate_Reporter()
    {
        var rnd = new Random(1);
        var items = Enumerable
            .Range(0, 10)
            .Select(x =&amp;gt; rnd.Next());
        Approvals.VerifyAll(items, &amp;quot;&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's change the seed to 2 to fail the test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var rnd = new Random(2);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if we run the test with NUnit3-Console.exe, we'll get an exception saying that ApprovalTests can't find Visual Studio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nunit3-console.exe .\bin\debug\environmentaltests.dll

...
1) Error : EnvironmentalTests.When_Running_In_Different_Environments.Delegates_To_Most_Appropriate_Reporter
System.Exception : Couldn't find Visual Studio at
at ApprovalTests.Reporters.GenericDiffReporter.Report(String approved, String received) in C:\code\ApprovalTests.Net\ApprovalTests\Reporters\GenericDiffReporter.cs:line 142
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It sounds worse than it is. ApprovalTests insists that it has &lt;em&gt;something&lt;/em&gt; to do. We can add a reporter to the fixture to fix it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
[UseReporter(typeof(VisualStudioReporter), typeof(NUnitReporter))]
public class When_Running_In_Different_Environments
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now when we run the test, we get a pure assertion failure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1) Failed : EnvironmentalTests.When_Running_In_Different_Environments.Delegates_To_Most_Appropriate_Reporter
Expected string length 165 but was 162. Strings differ at index 6.
Expected: &amp;quot;[0] = 534011718\n[1] = 237820880\n[2] = 1002897798\n[3] = 165700...&amp;quot;
But was:  &amp;quot;[0] = 1655911537\n[1] = 867932563\n[2] = 356479430\n[3] = 211537...&amp;quot;
-----------------^
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice it didn't even mention Visual Studio. Going back into our favorite IDE will start popping diffs again, and the NUnit one will govern the output.&lt;/p&gt;
&lt;h2&gt;Cleaning up&lt;/h2&gt;
&lt;p&gt;So should we go around declaring at least two reporters on all our fixtures, then? Luckily not. There are two more tricks that are nice to know.&lt;/p&gt;
&lt;p&gt;First, the reporter attribute is found by iterating up the inheritance hierarchy. It can be defined as high up as all your &lt;code&gt;[assembly:XAttribute]&lt;/code&gt; metadata. You can create a file in your test project root called &lt;code&gt;ApprovalsConfig.cs&lt;/code&gt; for instance. Within it, you declare your reporters on the assembly level:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using ApprovalTests.Reporters;

[assembly:UseReporter(typeof(VisualStudioReporter), typeof(NUnitReporter))]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second is that you might not want the NUnit assertion when you run in VS (for some reason), or maybe some other tool might be in your way. You might even want composite reporters. In any case, it also makes for a bit nicer setup if you implement the &lt;code&gt;FirstWorkingReporter&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class EnvironmentReporter : FirstWorkingReporter
{
    public static readonly EnvironmentReporter INSTANCE = new EnvironmentReporter();

    public EnvironmentReporter()
        : base(
            VisualStudioReporter.INSTANCE,
            NUnitReporter.INSTANCE
        )
    {
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With it, we can change our &lt;code&gt;UseReporter&lt;/code&gt; to be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[assembly:UseReporter(typeof(EnvironmentReporter))]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the first reporter to confirm its environment is executed, and the rest are ignored. You might want to keep the NUnit one around, though. In that case, you can implement &lt;code&gt;MultiReporter&lt;/code&gt; in the same way - which coincidentally is the same that &lt;code&gt;UseReporterAttribute&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;I encourage you to go have a look at &lt;a href="https://github.com/approvals/ApprovalTests.Net/tree/master/ApprovalTests/Reporters"&gt;all the built-in reporters&lt;/a&gt; and get some inspiration for even more helpful reporting.&lt;/p&gt;
&lt;p&gt;I'd rejoice for any cool usages in the discussion thread. :)&lt;/p&gt;
</description>
      <pubDate>Sun, 03 Jun 2018 21:24:46 Z</pubDate>
      <a10:updated>2018-06-03T21:24:46Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1138</guid>
      <link>https://blog.aabech.no/archive/morphing-ucommerce-products/</link>
      <category>umbraco</category>
      <category>ucommerce</category>
      <title>Morphing UCommerce Products</title>
      <description>&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Today I had a new but fun challenge with UCommerce. Turns out, as usual, it's a great fit for my whims with architecture.
I was stuck between a rock and a hard place when I was looking at adding a custom pricing algoritm.
I can't go into details, but there's custom client pricing involved of course.
To add to the fun, we're mapping UCommerce products to DTOs for wire transfer. We could aslo have been mapping to view models or something else. To map we're using AutoMapper with quite a few configurations and jumps-through-hoops.&lt;/p&gt;
&lt;p&gt;I had this code (ish):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var products = productRepository.Select().Where(SomePredicate);
var mapped = products.Select(Mapper.Map&amp;lt;ProductDto&amp;gt;);
return mapped;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I immediately thought of a few options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Iterate over the products and change prices here&lt;/li&gt;
&lt;li&gt;Create a Product adapter with additional logic and map from that&lt;/li&gt;
&lt;li&gt;Execute the pricing logic from AutoMapper configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They all seemed weird and out of place though. None seemed like they would be easy to find for the next developer. Not even with unit tests. It just didn't seem right.
Changing data on the entities would mean I'd have to go out of my way to ensure nobody went and saved those products later in the request. Creating an adapter would mean loads of new instances, bloated wrapper classes and weird names.
And finally executing business logic from AutoMapper configuration means I'd been mixing responsibilities en mass.&lt;/p&gt;
&lt;h2&gt;UCommerce &amp;amp; NHibernate to the rescue&lt;/h2&gt;
&lt;p&gt;Luckily I've been using EntityFramework a lot and tried to force it into my Domain Driven Design patterns since it's infancy. I've been through the lot (and I enjoy it). So I kind of know what to expect from an ORM. When using UCommerce I'm stuck with NHibernate, but I haven't really been doing it justice by just leaving it in the background. (And fiddling with Entity Framework - which is just as good!)
Together the two systems are extremely powerful. UCommerce have even documented the possibilities,
though the documentation fails to point out the really juicy benefits.&lt;/p&gt;
&lt;p&gt;We have &lt;code&gt;ProductDefintion&lt;/code&gt;, right? It allows us to set up product types with different properties and variant options. It even supports inheritance. But we're still stuck with the &lt;code&gt;Product&lt;/code&gt; class and its &lt;code&gt;GetProperty()&lt;/code&gt; overrides. In my case, I'd like to have &lt;code&gt;ProductWithFancyPricing&lt;/code&gt; so I could override that &lt;code&gt;GetPrice()&lt;/code&gt; method. If I could have &lt;code&gt;ProductWithFancyPricing&lt;/code&gt; and &lt;code&gt;ProductWithEvenFancierPricing&lt;/code&gt; that would be totally awesome.&lt;/p&gt;
&lt;p&gt;Turns out you can have your cake and eat it too. When properly using an ORM you can exploit OOP like it's supposed to and use polymorphism for varying behavior. It's possible to set up an inheritance tree so the mapper automatically handles creation of different types for you. You've basically got three options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Table per concrete class (type)
&lt;ul&gt;
&lt;li&gt;All classes have a table of their own&lt;/li&gt;
&lt;li&gt;Useful when base classes don't have (much) data&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Table per hierarchy
&lt;ul&gt;
&lt;li&gt;One table per base class&lt;/li&gt;
&lt;li&gt;Useful when &lt;em&gt;all&lt;/em&gt; data is on the base class&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Table per subclass
&lt;ul&gt;
&lt;li&gt;One common table for base data&lt;/li&gt;
&lt;li&gt;Individual tables per derived class with only additional data&lt;/li&gt;
&lt;li&gt;Useful when there are some data in both classes. (Think umbracoNode)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my case, I don't need any new data on the derived classes. It's all there in &lt;code&gt;GetProperty()&lt;/code&gt; anyway.
&lt;em&gt;(I will add some getters though. ModelsBuilder, anyone? )&lt;/em&gt;&lt;br /&gt;
So for me it's going to be Table per hierarchy. The rest of the options are all viable for this technique if you have other requirements.
You can &lt;a href="https://docs.ucommerce.net/ucommerce/v7.12/extending-ucommerce/extending-ucommerce-entities.html"&gt;read a bit about it in the UCommerce docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Mapping some product types&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;(I inadvertently wrote document types there. ModelsBuilder, anyone?)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In order to have NHibernate treat products as subclasses with the Table per hierarchy strategy it needs a way to pick the right class for each record. That way is known as discriminator columns. I first thought I could just discriminate by the ProductDefinitionId, but it turns out NHibernate doesn't support discriminating on a column already in use for associations (foreign keys) or other means.&lt;br /&gt;
We have to add a column. I just call it &amp;quot;Discriminator&amp;quot; and make it a varchar.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alter table uCommerce_Product add Discriminator nvarchar(max)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we need some entities. I added a couple of docu... product types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ProductWithFancyPricing : UCommerce.EntitiesV2.Product
{
    public override Money GetPrice(PriceGroup priceGroup)
    {
        var price = base.GetPrice(priceGroup);
        if (IsChristmas())
        {
            price = new Money(price.Value * 2, price.Culture, price.Currency);
        }
        return base.GetPrice(priceGroup);
    }
}

public class ProductWithEvenFancierPricing : UCommerce.EntitiesV2.Product
{
    public override Money GetPrice(PriceGroup priceGroup)
    {
        var blackMarket = ObjectFactory.Instance.Resolve&amp;lt;IBlackMarketService&amp;gt;();
        var priceValue = blackMarket.GetPrice(Sku);
        return new Money(priceValue, priceGroup.Currency);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The next thing you need is to tell NHibernate that these are our new product classes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ProductWithFancyPricingMapping : FluentNHibernate.Mapping.SubclassMap&amp;lt;ProductWithFancyPricing&amp;gt;
{
    public ProductWithFancyPricingMapping()
    {
        DiscriminatorValue(&amp;quot;Product with fancy pricing&amp;quot;);
    }
}

public class ProductWithEvenFancierPricingMapping : FluentNHibernate.Mapping.SubclassMap&amp;lt;ProductWithEvenFancierPricing&amp;gt;
{
    public ProductWithEvenFancierPricingMapping()
    {
        DiscriminatorValue(&amp;quot;Product with naughty pricing&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We also need to subclass UCommerce's mapping for Product in order to tell UCommerce which column to use as the discriminator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ProductMap : global::UCommerce.EntitiesV2.Maps.ProductMap
{
    public ProductMap()
    {
        DiscriminateSubClassesOnColumn(&amp;quot;Discriminator&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally we need a class in the same assembly with a tag on it. &lt;a href="https://docs.ucommerce.net/ucommerce/v7.12/extending-ucommerce/save-custom-data-in-the-database.html"&gt;More on that in the UCommerce docs&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MappingMarker : IContainsNHibernateMappingsTag
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To have NHibernate pick the right classes now, we just need to fix the existing products if we have any.
I have called my discriminator values the same as my document types, so I can easily construct a query as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;update
    ucommerce_product
set 
    discriminator = case productdefinitionid
        when 10 then 'Product with fancy pricing'
        when 11 then 'Product with naughty pricing'
        else null
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if we go...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var products = productRepository.Select();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;...we'll get a bounch of &lt;code&gt;ProductWithFancyPricings&lt;/code&gt; and &lt;code&gt;ProductWithEvenFancierPricing&lt;/code&gt;.
If you have more types, you might get into trouble though. You need to have a discriminator on them all.&lt;/p&gt;
&lt;h2&gt;The final hurdle&lt;/h2&gt;
&lt;p&gt;So that's cool. That's really cool. But there's one hurdle we have to jump over before we can cross the goal line. From very nasty experiences I knew I had to test &lt;em&gt;everything&lt;/em&gt; manually and integrated. So I went and tried to see what happened if I added a product through the UCommerce Admin UI.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;boom&lt;/em&gt; :)&lt;/p&gt;
&lt;p&gt;'Course it didn't work. It actually did, and didn't. Several weird things happened ranging from strange NHibernate mapping exceptions to products getting the discriminator &amp;quot;UCommerce.EntitiesV2.Product&amp;quot;. (Which makes a lot of sense if you think about it)&lt;/p&gt;
&lt;p&gt;The @#¤%&amp;amp; &lt;code&gt;CreateCategoryOrProduct.as[p|c]x&lt;/code&gt; WebForms control is in our way. It instantiates a &lt;code&gt;Product&lt;/code&gt; and saves it. It's completely sealed and unconfigurable. We could overwrite it with a custom one, but that would open another can of worms with regards to upgrading, source control and what-not. Luckily it's going away very very soon in UCommerce V8. (2018?)&lt;/p&gt;
&lt;p&gt;After hacking at it a bit my final resolve was to add a step right after save in the product saving pipeline. Again, UCommerce is so versatile that even when it sucks, it's got a golden workaround right up its arm.
If you're not familiar with UCommerce Pipelines, &lt;a href="https://docs.ucommerce.net/ucommerce/v7.12/extending-ucommerce/create-pipeline-task.html"&gt;go read about it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's the extra configuration. (In a .config file included from UCommerce's custom.config)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- PRODUCT CLASS FIX --&amp;gt;
&amp;lt;component id=&amp;quot;SaveProduct&amp;quot;
           service=&amp;quot;UCommerce.Pipelines.IPipeline`1[[UCommerce.EntitiesV2.Product, UCommerce]], UCommerce&amp;quot;
           type=&amp;quot;UCommerce.Pipelines.Catalog.ProductPipeline, UCommerce.Pipelines&amp;quot;&amp;gt;
  &amp;lt;parameters&amp;gt;
    &amp;lt;tasks&amp;gt;
      &amp;lt;array&amp;gt;
        &amp;lt;value&amp;gt;${Product.UpdateRevision}&amp;lt;/value&amp;gt;
        &amp;lt;value&amp;gt;${Product.Save}&amp;lt;/value&amp;gt;
        &amp;lt;value&amp;gt;${FixProductClass}&amp;lt;/value&amp;gt;
        &amp;lt;value&amp;gt;${Product.IndexAsync}&amp;lt;/value&amp;gt;
      &amp;lt;/array&amp;gt;
    &amp;lt;/tasks&amp;gt;
  &amp;lt;/parameters&amp;gt;
&amp;lt;/component&amp;gt;

&amp;lt;component id=&amp;quot;FixProductClass&amp;quot;
           service=&amp;quot;UCommerce.Pipelines.IPipelineTask`1[[UCommerce.EntitiesV2.Product, UCommerce]], UCommerce&amp;quot;
           type=&amp;quot;My.Awesome.Site.Persistence.FixProductClassTask, My.Awesome.Site.UCommerce&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A pipeline task gets a reference to the entity being handled, so we can't just go and replace the entire product with an instance of the right type. But we can fake it and force the database value to be correct after saving.
UCommerce uses NHibernate level 2 cache, so we need to flush that as well, but we'll get to that.&lt;/p&gt;
&lt;p&gt;Forcing the database is fairly easy. We have to resort to good old ADO code, which was actually a joyful little deja-vu experience (although I'm glad it was brief):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class FixProductClassTask : IPipelineTask&amp;lt;Product&amp;gt;
{
    private readonly IStatelessSessionProvider sessionProvider;

    public FixProductClassTask(IStatelessSessionProvider sessionProvider)
    {
        this.sessionProvider = sessionProvider;
    }

    public PipelineExecutionResult Execute(Product subject)
    {
        var command = sessionProvider.GetStatelessSession().Connection.CreateCommand();
        command.CommandText = &amp;quot;UPDATE uCommerce_Product SET Discriminator = @discriminator WHERE ProductId = @productId&amp;quot;;
        command.CommandType = CommandType.Text;
        var discriminatorParam = command.CreateParameter();
        discriminatorParam.ParameterName = &amp;quot;discriminator&amp;quot;;
        discriminatorParam.Value = subject.ProductDefinition.Name;
        var idParam = command.CreateParameter();
        idParam.ParameterName = &amp;quot;productId&amp;quot;;
        idParam.Value = subject.Id;
        command.Parameters.Add(discriminatorParam);
        command.Parameters.Add(idParam);
        command.ExecuteNonQuery();

        // TODO: Clear cache

        return PipelineExecutionResult.Success;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm sure a lot of sazzy devs out there could prettify this a bit, but it does the job. Insert a &lt;a href="/archive/kill-switch-weve-got-action/"&gt;switch/case (please don't)&lt;/a&gt; or whatever you fancy if the product definition name isn't what you discriminate by. I'll leave it up to you to choose between strings, ints or even enums for performance vs. readability.&lt;/p&gt;
&lt;p&gt;If you've turned off the level 2 cache, you might be fine with this. Otherwise we'd better &amp;quot;evict&amp;quot; the entity from the cache. We need to do that in order for the cached instance to change type from &lt;code&gt;Product&lt;/code&gt; to &lt;code&gt;ProductWithFancyPricing&lt;/code&gt;. Sadly the NHibernate &lt;code&gt;SessionFactory&lt;/code&gt; in charge of doing this is hidden in an internal static field in UCommerce, so we need to resort to some nasty reflection to do it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
command.ExecuteNonQuery();

var fieldInfo = typeof(SessionProvider).GetField(&amp;quot;_factory&amp;quot;, BindingFlags.Static | BindingFlags.NonPublic);
if (fieldInfo == null) throw new Exception(&amp;quot;SessionFactory instance has moved in this UCommerce version. %(&amp;quot;);
var sessionFactory = (ISessionFactory)fieldInfo.GetValue(null);
sessionFactory.Evict(typeof(Product), subject.Id);

return PipelineExecutionResult.Success;
// ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Et voilá! We can now save new products, and they immediately morph into the correct derived type.
(Except for when being saved to RavenDB for the first time, ref. the config).&lt;/p&gt;
&lt;p&gt;I'm now free to go back into the instances and implement however naughty pricing I fancy. \o/&lt;/p&gt;
&lt;h2&gt;Added bonuses&lt;/h2&gt;
&lt;p&gt;I already &lt;a href="https://docs.ucommerce.net/ucommerce/v7.12/extending-ucommerce/save-custom-data-in-the-database.html"&gt;have a custom entity in the database and NHibernate model&lt;/a&gt;. It has two associations to &lt;code&gt;Product&lt;/code&gt;. Had I realized what I had under my fingertips it would already have been collections on my new shiny subclasses.&lt;/p&gt;
&lt;p&gt;I recon you noticed I referenced ModelsBuilder a couple of times. How 'bout having all your properties statically typed on your product instances. How about some interfaces?&lt;/p&gt;
&lt;p&gt;I'm sure you're getting the drift.&lt;/p&gt;
&lt;p&gt;I for one am quite embarrased I didn't think of this before. I've had the knowledge and tools for years. But there you go. We learn something every day. And I love doing it with Umbraco, UCommerce, EntityFramework and apparently now also... NHibernate. :)&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Jan 2018 23:48:27 Z</pubDate>
      <a10:updated>2018-01-30T23:48:27Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1137</guid>
      <link>https://blog.aabech.no/archive/umbraco-unit-testing-workshop-material/</link>
      <category>umbraco</category>
      <category>unit testing</category>
      <title>Umbraco Unit Testing Workshop Material</title>
      <description>&lt;p&gt;For this years Umbraco UK Festival I was honored to be invited to run a unit testing workshop. It's a culmination of my talks and workshops until now, and covers everything you need to get going with testing the Umbraco backoffice, controller logic, external calls and finally front-end UI.&lt;/p&gt;
&lt;p&gt;I know there's demand for this kind of training, and it might be difficult to get to the conferences where it's happening. For those of you, and those who feel bold and confident, I've released the material under the MIT license.&lt;/p&gt;
&lt;p&gt;I can't promise I'll be able to help out if you go at it, but keep an eye out for a conference near you and encourage the organizers to host a workshop. ;)&lt;/p&gt;
&lt;p&gt;You can clone the &amp;quot;workshop-start&amp;quot; branch from Bitbucket. The slides and workbook are included at the root of the repo.&lt;/p&gt;
&lt;p&gt;https://bitbucket.org/bleedo/umbraco-testing-workshop-h2-2017/src/?at=workshop-start&lt;/p&gt;
&lt;p&gt;You'll need&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;node&lt;/li&gt;
&lt;li&gt;npm task runner (visual studio extension)&lt;/li&gt;
&lt;li&gt;razorgenerator (visual studio extension)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
</description>
      <pubDate>Thu, 09 Nov 2017 10:09:16 Z</pubDate>
      <a10:updated>2017-11-09T10:09:16Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1136</guid>
      <link>https://blog.aabech.no/archive/automating-umbraco-with-powershell/</link>
      <category>umbraco</category>
      <category>automation</category>
      <title>Automating Umbraco with PowerShell</title>
      <description>&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;This particular example is for warming up a UCommerce site in staging, but the technique can be used for anything you can do in the Umbraco backoffice. Especially with the new REST APIs coming out.&lt;/p&gt;
&lt;p&gt;In this particular case, I've sinned and not created a good test environment for the last few integration bits of a project. It was hard to tune the production behavior of some code without actually running it in production. However, it's running Umbraco 7.5.13 and UCommerce 7.7. It's probably missing the other performance fix too, but the result is that it takes quite a while to warm up everything. So we warm it up in staging and then swap slots to get it fresh, awake and blazing fast into production. &lt;/p&gt;
&lt;h2&gt;Resolve&lt;/h2&gt;
&lt;p&gt;After having done this a few times, I figured I wanted something to do while waiting. What better activity than automating the whole routine so I could do something else instead? (Like automating automation...) Here's a powershell script I ended up with to warm up everything in the backoffice. I'll go through the pieces below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Param(
    [string]$password
)

$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

$body = @{
    &amp;quot;username&amp;quot;=&amp;quot;admin@admin.com&amp;quot;
    &amp;quot;password&amp;quot;=$password
}

$json = $body | ConvertTo-Json

Invoke-RestMethod `
    -Method Post `
    -ContentType &amp;quot;application/json&amp;quot; `
    -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin&amp;quot; `
    -WebSession $ws `
    -Body $json

Write-Host &amp;quot;20%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718&amp;quot; -WebSession $ws

Write-Host &amp;quot;40%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editproduct.aspx?id=465&amp;amp;parentcategoryId=718&amp;quot; -WebSession $ws

Write-Host &amp;quot;60%&amp;quot;
Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editpaymentmethod.aspx?id=8&amp;quot; -WebSession $ws

Write-Host &amp;quot;80%&amp;quot;

Invoke-RestMethod -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/settings/orders/editshippingmethod.aspx?id=10&amp;quot; -WebSession $ws
Write-Host &amp;quot;100%&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The effect is that we log into Umbraco using a provided password, and instead of navigating and clicking everything, we fire a request triggering all the caching and JIT compilation for us. Even though I'm using the &lt;code&gt;Invoke-RestMethod&lt;/code&gt; cmdlet, I can do regular web calls. The cmdlet has a sibling called &lt;code&gt;Invoke-WebRequest&lt;/code&gt;, but the rest version is better for posting commands. It's mostly a matter of mental context, but they have a few differences.&lt;/p&gt;
&lt;h2&gt;Log into Umbraco&lt;/h2&gt;
&lt;p&gt;To set up an authorized session with Umbraco, we can call the PostLogin action. It's the same endpoint that is used from the login screen. An authorized session means that we need to get a cookie and pass it with all our requests. In order for each &lt;code&gt;Invoke-RestMethod&lt;/code&gt; to pass this cookie, we can create a &lt;code&gt;WebRequestSession&lt;/code&gt; we pass to each call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ws = New-Object Microsoft.PowerShell.Commands.WebRequestSession

Invoke-RestMethod -WebSession $ws -Uri &amp;quot;...&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the response brings a cookie, it's kept in the &lt;code&gt;WebRequestSession&lt;/code&gt;, and subsequently passed back with each new request. Just like a browser.&lt;/p&gt;
&lt;p&gt;Then we need some JSON to pass our username and password. You can declare dictionaries of sorts in PowerShell like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$body = @{
    &amp;quot;username&amp;quot;=&amp;quot;admin@admin.com&amp;quot;
    &amp;quot;password&amp;quot;=$password
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then convert it to JSON by piping it to the &lt;code&gt;ConvertTo-Json&lt;/code&gt; cmdlet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$json = $body | ConvertTo-Json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally we're ready to fire the request off to Umbraco, adding config for HTTP method, ContentType etc.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Invoke-RestMethod `
    -Method Post `
    -ContentType &amp;quot;application/json&amp;quot; `
    -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/backoffice/UmbracoApi/Authentication/PostLogin&amp;quot; `
    -WebSession $ws `
    -Body $json
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Automate Umbraco&lt;/h2&gt;
&lt;p&gt;For this I just needed to kick off a request to some pages, but posting messages around like rebuilding a grumpy index, running an ad hoc task, even publishing should be just as simple.&lt;/p&gt;
&lt;p&gt;It takes a while, so I added a little status message. I'm sure PowerShell wizards would pack this stuff into better reusable parts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Write-Host &amp;quot;20%&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now reuse that &lt;code&gt;WebRequestSession&lt;/code&gt; object to fire off new &lt;em&gt;authenticated&lt;/em&gt; requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Invoke-RestMethod -WebSession $ws -Method Get -Uri &amp;quot;https://customer-staging.azurewebsites.net/umbraco/ucommerce/catalog/editcategory.aspx?id=718&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Do it everywhere&lt;/h2&gt;
&lt;p&gt;With all the love I can give to UCommerce, I ended up naming the script &lt;code&gt;kick-ucommerce.ps1&lt;/code&gt;. It's like kicking your old belowed car to get it started, after you've polished it. Really!&lt;br /&gt;
Adding my source folder to the path environment variable makes the script available from any shell. Even the Package Manager console in Visual Studio. &lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1034/warmup-ucommerce.png" alt="Warm up UCommerce" /&gt;&lt;/p&gt;
&lt;p&gt;Make a note that the password is a parameter. You do change it more often than you'd like to update the code right? How 'bout automating the process? ;)&lt;/p&gt;
&lt;h2&gt;Smoke test and swap the Azure slot&lt;/h2&gt;
&lt;p&gt;I'll leave the swap-slot script I run after warming up the site here too. The cool thing about warming up with the script is that it'll fail almost immediately on the Umbraco login if anything isn't like it should. So it doubles as a smoke test.&lt;/p&gt;
&lt;p&gt;When everything looks good, I can just go:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;swap-slot -from staging -to production -site customer-x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here's the &amp;quot;simpleness&amp;quot; of that one. There are fairly good docs on all the Azure cmdlets over on Microsoft's sites. (Ask Google. ;) )&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Param(
    [string]$site,
    [string]$from,
    [string]$to
)

$subscriptionId = &amp;quot;333112F4-4483-449C-A2DA-727E8D2E428D&amp;quot;
$resourcegroupname = &amp;quot;Common-Group&amp;quot;     # Might need to be param

Login-AzureRmAccount -SubscriptionId $subscriptionId

Swap-AzureRmWebAppSlot `
    -SourceSlotName $from `
    -DestinationSlotName $to `
    -Name $site `
    -ResourceGroupName $resourcegroupname
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's it! Now go automate something so you get more time to do fun stuff! :)&lt;/p&gt;
</description>
      <pubDate>Tue, 07 Nov 2017 19:46:14 Z</pubDate>
      <a10:updated>2017-11-07T19:46:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1133</guid>
      <link>https://blog.aabech.no/archive/exploiting-approvaltests-for-clearer-tests/</link>
      <category>unit testing</category>
      <title>Exploiting ApprovalTests for clearer tests</title>
      <description>&lt;h2&gt;What's this for?&lt;/h2&gt;
&lt;p&gt;Ever written several asserts in one test because you have a big graph you want to verify? How 'bout files or maybe &lt;a href="/archive/testing-views-with-razorgenerator"&gt;razor views&lt;/a&gt;? Several asserts often clutter up the tests. Big strings also make the actuall calls hard to see for all that content. Putting big strings in files is a good idea to avoid that, but few people do. It adds another &amp;quot;menial&amp;quot; task when you're in the zone. But what if it was dead easy?&lt;/p&gt;
&lt;p&gt;I once maintained a Resharper extension, and their example tests had so called &amp;quot;gold&amp;quot; files that they compared to output. The squigglies was represented by special characters around terms. So they just compared the output of a text renderer to the text in a file. Great idea. One limitation though: with a regular Assert.Equals you just see the segment around the first mismatch. Guess what, there's a tool that's been around for at least 10 years that solves all those issues, and more.&lt;/p&gt;
&lt;h2&gt;Approval tests, eh?&lt;/h2&gt;
&lt;p&gt;Sounds a bit like acceptance tests, right? Don't be fooled, it's purpose is to serve all the way down to the &amp;quot;unit test layer&amp;quot; of your tests. I've found it to be a huge timesaver, as well as making my tests so much more clear.&lt;/p&gt;
&lt;p&gt;I know you're thinking &amp;quot;Shut up and give me an example, then!&amp;quot;, so let's have a look. I've got this unit test from my post about &lt;a href="/archive/testing-views-with-razorgenerator"&gt;testing razor views&lt;/a&gt; that I never really asserted anything in. I just output the result to console and assert inconclusive. Here it is for reference:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
public class When_Displaying_An_Event
{
    [Test]
    public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
    {
        var view = new _Views_Partials_Event_cshtml();
        var actionResult = GetConcertEvent();

        Assert.AreEqual(&amp;quot;Event&amp;quot;, actionResult.ViewName);

        var renderedResult = view.Render((Event) actionResult.Model);

        Console.WriteLine(renderedResult);
        Assert.Inconclusive(&amp;quot;Way too big to assert here.&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It outputs the following HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
    &amp;lt;a href=&amp;quot;https://eventsite/123&amp;quot;&amp;gt;
        &amp;lt;label&amp;gt;
            Concert of your life
        &amp;lt;/label&amp;gt;
        &amp;lt;span&amp;gt;
            fredag 31. desember 2049 23.59
        &amp;lt;/span&amp;gt;
    &amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Let's see it&lt;/h2&gt;
&lt;p&gt;Now let's assert this with ApprovalTests. To use it, you just &lt;code&gt;install-package ApprovalTests&lt;/code&gt; with your trusty package manager console. Make sure to install it in your test project. ;)&lt;/p&gt;
&lt;p&gt;Now instead of &lt;code&gt;Assert&lt;/code&gt;, we ask ApprovalTests to &lt;code&gt;Verify&lt;/code&gt; our data instead. It even has a special overload for this concrete case: &lt;code&gt;Approvals.VerifyHtml&lt;/code&gt;. So we rewrite the test as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
{
    var view = new _Views_Partials_Event_cshtml();
    var actionResult = GetConcertEvent();

    Assert.AreEqual(&amp;quot;Event&amp;quot;, actionResult.ViewName);

    var renderedResult = view.Render((Event) actionResult.Model);

    Approvals.VerifyHtml(renderedResult);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now when we run our test, we get this nice little welcoming message from ApprovalTests:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1033/welcome-to-approvaltests.png" alt="Exception: Welcome to ApprovalTests" /&gt;&lt;/p&gt;
&lt;p&gt;It tells us we're missing a vital part: A reporter. It's possible to use &lt;code&gt;DiffReporter&lt;/code&gt; to launch your favorite configured difftool. But if you're in Visual Studio, there's a special treat: &lt;code&gt;VisualStudioReporter&lt;/code&gt;. Let's add that to our fixture and see what happens:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[UseReporter(typeof(VisualStudioReporter))]
[TestFixture]
public class When_Displaying_An_Event
{
    [Test]
    public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
    {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And we run it:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1032/first-result.png" alt="ApprovalTests first result have empty approved" /&gt;&lt;/p&gt;
&lt;p&gt;What hits you is probably the big failure statement there, but look again - up at the top there.
We've got a diff opened, the result on the left hand and a big green field on the right side.&lt;br /&gt;
What just happened is that ApprovalTests took our result, stored it in a received file, and at the same time wrote an empty &amp;quot;approved&amp;quot; file. It then proceeded to compare, and finally pop a diff of those two files.&lt;br /&gt;
Isn't that just beautiful? Everything that makes your test fail in one clear diagram.&lt;/p&gt;
&lt;p&gt;The &amp;quot;procedure to follow&amp;quot; here, is to &amp;quot;approve&amp;quot; results when you're happy. To do that, you just copy and paste. Let's do that now:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1030/first-approved.png" alt="First approved with invalid indenting" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you've got ReSharper, it's probably gonna try to format everything nicely when you paste. To have it in the original, ugly indentation state, press Ctrl+Z (undo) once after pasting.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Regarding the indentation that's off. It seems to be a bug with HTML in the current version of ApprovalTests, so stupid me for choosing this example. I'll update the post if it gets fixed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can now run our test again, and it's gonna pass. When it passes, it doesn't bother to open the diff.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1031/first-passing.png" alt="It passes with an approved file" /&gt;&lt;/p&gt;
&lt;p&gt;This means we're just gonna get diffs for whatever is currently failing. Even if we run our entire suite of tests. Now there's a couple of housekeeping things to keep in mind:&lt;/p&gt;
&lt;h2&gt;Commit approvals only&lt;/h2&gt;
&lt;p&gt;If you were paying attention, you noticed we got two files adjacent to our test source file. One is named [TestFixtureName].[TestMethodName].received.html and one is named [TestFixtureName].[TestMethodName].approved.html. If you ran a test that passed, you'll actually just have your approved file. &lt;strong&gt;You want those approved files in source control!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The received files though, might end up not being cleaned up for one or the other reason. Hopefully just that you didn't bother to commit a fully passing build. I'm sure you didn't do that to the master branch, though. In any case, make sure to &lt;em&gt;ignore&lt;/em&gt; your received files. This pattern in .gitignore generally does the trick:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;*.received.*&lt;/code&gt; &lt;/p&gt;
&lt;h2&gt;That's just the tip of the iceberg&lt;/h2&gt;
&lt;p&gt;We've seen the &lt;code&gt;VerifyHtml&lt;/code&gt; bit. One of my personal favorites is it's sibling &lt;code&gt;VerifyJson&lt;/code&gt;. I keep an extension on &lt;code&gt;object&lt;/code&gt; in my tests, called &lt;code&gt;.ToJson()&lt;/code&gt;. With it, I can just go:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Approvals.VerifyJson(myBigGraph.ToJson());
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The diff is done with prettified JSON, so it's super simple to find the property / area that has changed or doesn't work. Knowing the area of the graph should also make it easier to find the usage that is wrong.&lt;/p&gt;
&lt;p&gt;There's a vanilla &lt;code&gt;Verify&lt;/code&gt; method too, and it saves plain text files. It's useful in cases where you have nice &lt;code&gt;ToString()&lt;/code&gt; implementations. Let's try with a &amp;quot;person&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return $&amp;quot;{FirstName} {LastName} ({Age})&amp;quot;;
    }
}

[Test]
public void Comparing_Objects()
{
    var person = new Person
    {
        FirstName = &amp;quot;Lars-Erik&amp;quot;,
        LastName = &amp;quot;Aabech&amp;quot;,
        Age = 19
    };

    Approvals.Verify(person);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It produces the following .received file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lars-Erik Aabech (19)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It can be approved like that.&lt;br /&gt;
We can also do lists with &amp;quot;big brother&amp;quot; &lt;code&gt;VerifyAll&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
public void Comparing_Lists()
{
    var list = new List&amp;lt;Person&amp;gt;
    {
        new Person {FirstName = &amp;quot;Lars-Erik&amp;quot;, LastName = &amp;quot;Aabech&amp;quot;},
        new Person {FirstName = &amp;quot;Dear&amp;quot;, LastName = &amp;quot;Reader&amp;quot;}
    };

    Approvals.VerifyAll(list, &amp;quot;&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which, unsurprisingly outputs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[0] = Lars-Erik Aabech
[1] = Dear Reader
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now how bout that?&lt;br /&gt;
I don't know how quickly you got hooked, but I certainly find it sneaking into more and more of my tests.&lt;/p&gt;
&lt;p&gt;It can even do images, but I'll let &lt;a href="http://jamessouth.me/"&gt;James&lt;/a&gt; blog about that one.&lt;/p&gt;
&lt;p&gt;So what are you lingering around here for? Run over to nuget and get your copy, or lurk around some more at the &lt;a href="http://approvaltests.sourceforge.net/"&gt;ApprovalTests&lt;/a&gt; project site. There's great examples, even though they might not be in your favorite language.&lt;/p&gt;
&lt;p&gt;Happy approving! :)&lt;/p&gt;
</description>
      <pubDate>Mon, 23 Oct 2017 20:14:08 Z</pubDate>
      <a10:updated>2017-10-23T20:14:08Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1127</guid>
      <link>https://blog.aabech.no/archive/umbracosupport-got-typed-content/</link>
      <category>unit testing</category>
      <category>umbraco</category>
      <title>UmbracoSupport got typed content</title>
      <description>&lt;h2&gt;What's UmbracoSupport?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;UmbracoSupport&lt;/code&gt; is a class I've been introducing to my unit tests over the last year or so.
It allows me to have my own hierarchy for tests, as well as re-using all of Umbraco's own
stubbing code. I've written about it in a post called &lt;a href="/archive/the-basics-of-unit-testing-umbraco-just-got-simpler"&gt;Unit testing Umbraco just got simpler&lt;/a&gt;,
and its gut's code is described in details in &lt;a href="/archive/the-basics-of-unit-testing-umbraco"&gt;The basics of unit testing Umbraco&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A quick primer on what's already available&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;BaseDatabaseFactoryTest&lt;/code&gt; in &lt;code&gt;Umbraco.Tests&lt;/code&gt; has a method called &lt;code&gt;GetXmlContent&lt;/code&gt;.
It replaces the &lt;code&gt;umbraco.config&lt;/code&gt; file that acts as the cache at runtime.
It makes &lt;code&gt;UmbracoContext.Current.ContentCache&lt;/code&gt; tick in unit tests.
The base tests out of the box has a small flaw though. They can't &amp;quot;popuplate&amp;quot; properties.
All you get is the hierarchy.&lt;/p&gt;
&lt;p&gt;Usually I've injected an &lt;code&gt;IPublishedContentCache&lt;/code&gt; to my controllers. When testing them,
I've created a mock instance of the &lt;code&gt;IPublishedContentCache&lt;/code&gt;. However, all my code has to use
the non-context aware overloads. For instance &lt;code&gt;cache.GetById(umbracoContext, false, id)&lt;/code&gt;.
There's also a whole lot of ugly mocking code going on to set up queries and stubbed content.
How to stub properties on stubbed content is described in &lt;a href="/archive/slides-from-cg16-and-testing-ipublishedcontent-properties/"&gt;Slides from CG 2016 and testing IPublishedContent properties&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;So what's new?&lt;/h2&gt;
&lt;p&gt;As mentioned, I've been throwing around all kinds of ugly stubbing code for content and I've also been tampering with &lt;code&gt;Umbraco.Tests&lt;/code&gt;'s &lt;code&gt;GetXmlContent()&lt;/code&gt; to use the &amp;quot;built-in&amp;quot; content stubs.
It's all been done before in misc. tests in Umbraco. I finally got my s**t together and refactored all my setup spaghetti into a few small helpers on the &lt;code&gt;UmbracoSupport&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Let's go over them in increasing &amp;quot;integrationness&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Pure hierarchy&lt;/h2&gt;
&lt;p&gt;Your basic hierarchy structure can be set up by simply returning a string from an overload of &lt;code&gt;BaseDatabaseFactoryTest.GetXmlContent&lt;/code&gt;. &lt;code&gt;UmbracoSupport&lt;/code&gt; overloads this method and returns whatever you've set on the &lt;code&gt;UmbracoSupport.ContentCacheXml&lt;/code&gt; property. I recommend using the technique described in &lt;a href="/archive/automating-creation-of-source-data-for-tests"&gt;Automating creating of source data for tests&lt;/a&gt; with this. You can even extend that code to have fixture specific content caches.&lt;/p&gt;
&lt;p&gt;In any case, to make this work, you just need to set the XML in the setup method.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I've got some probs with the markdown parsing here, imagine the CDATA parts of the XML is correctly written.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[SetUp]
public void Setup()
{
    umbracoSupport = new UmbracoSupport();
    umbracoSupport.SetupUmbraco();

    // This XML is what the ContentCache will represent
    umbracoSupport.ContentCacheXml = @&amp;quot;
        &amp;lt;?xml version=&amp;quot;&amp;quot;1.0&amp;quot;&amp;quot; encoding=&amp;quot;&amp;quot;utf-8&amp;quot;&amp;quot;?&amp;gt;
        &amp;lt;!DOCTYPE root [
          &amp;lt;!ELEMENT contentBase ANY&amp;gt;
          &amp;lt;!ELEMENT home ANY&amp;gt;
          &amp;lt;!ATTLIST home id ID #REQUIRED&amp;gt;
          &amp;lt;!ELEMENT page ANY&amp;gt;
          &amp;lt;!ATTLIST page id ID #REQUIRED&amp;gt;
        ]&amp;gt;
        &amp;lt;root id=&amp;quot;&amp;quot;-1=&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;gt;
          &amp;lt;home id=&amp;quot;&amp;quot;1103=&amp;quot;&amp;quot;&amp;quot;&amp;quot; key=&amp;quot;&amp;quot;156f1933-e327-4dce-b665-110d62720d03=&amp;quot;&amp;quot;&amp;quot;&amp;quot; parentID=&amp;quot;&amp;quot;-1=&amp;quot;&amp;quot;&amp;quot;&amp;quot; level=&amp;quot;&amp;quot;1=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; sortOrder=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; createDate=&amp;quot;&amp;quot;2017-10-17T20:25:12=&amp;quot;&amp;quot;&amp;quot;&amp;quot; updateDate=&amp;quot;&amp;quot;2017-10-17T20:25:17=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeName=&amp;quot;&amp;quot;Home=&amp;quot;&amp;quot;&amp;quot;&amp;quot; urlName=&amp;quot;&amp;quot;home=&amp;quot;&amp;quot;&amp;quot;&amp;quot; path=&amp;quot;&amp;quot;-1,1103=&amp;quot;&amp;quot;&amp;quot;&amp;quot; isDoc=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeType=&amp;quot;&amp;quot;1093=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; template=&amp;quot;&amp;quot;1064=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeTypeAlias=&amp;quot;&amp;quot;home=&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;gt;
            &amp;lt;title&amp;gt;Welcome!&amp;lt;/title&amp;gt;
            &amp;lt;excerptCount&amp;gt;4&amp;lt;/excerptCount&amp;gt;
            &amp;lt;page id=&amp;quot;&amp;quot;1122=&amp;quot;&amp;quot;&amp;quot;&amp;quot; key=&amp;quot;&amp;quot;1cb33e0a-400a-4938-9547-b05a35739b8b=&amp;quot;&amp;quot;&amp;quot;&amp;quot; parentID=&amp;quot;&amp;quot;1103=&amp;quot;&amp;quot;&amp;quot;&amp;quot; level=&amp;quot;&amp;quot;2=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; sortOrder=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; createDate=&amp;quot;&amp;quot;2017-10-17T20:25:12=&amp;quot;&amp;quot;&amp;quot;&amp;quot; updateDate=&amp;quot;&amp;quot;2017-10-17T20:25:17=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeName=&amp;quot;&amp;quot;Page=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot;&amp;quot;&amp;quot; urlName=&amp;quot;&amp;quot;page1=&amp;quot;&amp;quot;&amp;quot;&amp;quot; path=&amp;quot;&amp;quot;-1,1103,1122=&amp;quot;&amp;quot;&amp;quot;&amp;quot; isDoc=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeType=&amp;quot;&amp;quot;1095=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; template=&amp;quot;&amp;quot;1060=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeTypeAlias=&amp;quot;&amp;quot;page=&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;gt;
              &amp;lt;title&amp;gt;Welcome!&amp;lt;/title&amp;gt;
              &amp;lt;excerpt&amp;gt;[CDATA[Lorem ipsum dolor...]]&amp;lt;/excerpt&amp;gt;
              &amp;lt;body&amp;gt;
                [CDATA[&amp;lt;p&amp;gt;Lorem ipsum dolor...&amp;lt;/p&amp;gt;]]
              &amp;lt;/body&amp;gt;
              &amp;lt;image&amp;gt;123&amp;lt;/image&amp;gt;
            &amp;lt;/page&amp;gt;
            &amp;lt;page id=&amp;quot;&amp;quot;1123=&amp;quot;&amp;quot;&amp;quot;&amp;quot; key=&amp;quot;&amp;quot;242928f6-a1cf-4cd3-ac34-f3ddf3526b2e=&amp;quot;&amp;quot;&amp;quot;&amp;quot; parentID=&amp;quot;&amp;quot;1103=&amp;quot;&amp;quot;&amp;quot;&amp;quot; level=&amp;quot;&amp;quot;2=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; sortOrder=&amp;quot;&amp;quot;1=&amp;quot;&amp;quot;&amp;quot;&amp;quot; createDate=&amp;quot;&amp;quot;2017-10-17T20:25:12=&amp;quot;&amp;quot;&amp;quot;&amp;quot; updateDate=&amp;quot;&amp;quot;2017-10-17T20:25:17=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeName=&amp;quot;&amp;quot;Page=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot;&amp;quot;&amp;quot; urlName=&amp;quot;&amp;quot;page2=&amp;quot;&amp;quot;&amp;quot;&amp;quot; path=&amp;quot;&amp;quot;-1,1103,1123=&amp;quot;&amp;quot;&amp;quot;&amp;quot; isDoc=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeType=&amp;quot;&amp;quot;1095=&amp;quot;&amp;quot;&amp;quot;&amp;quot; creatorName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerName=&amp;quot;&amp;quot;Admin=&amp;quot;&amp;quot;&amp;quot;&amp;quot; writerID=&amp;quot;&amp;quot;0=&amp;quot;&amp;quot;&amp;quot;&amp;quot; template=&amp;quot;&amp;quot;1060=&amp;quot;&amp;quot;&amp;quot;&amp;quot; nodeTypeAlias=&amp;quot;&amp;quot;page=&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;gt;
              &amp;lt;title&amp;gt;More welcome!&amp;lt;/title&amp;gt;
              &amp;lt;excerpt&amp;gt;[CDATA[More lorem ipsum dolor...]]&amp;lt;/excerpt&amp;gt;
              &amp;lt;body&amp;gt;[CDATA[Even more lorem ipsum dolor...]]&amp;lt;/body&amp;gt;
              &amp;lt;image&amp;gt;234&amp;lt;/image&amp;gt;
            &amp;lt;/page&amp;gt;
          &amp;lt;/home&amp;gt;
        &amp;lt;/root&amp;gt;
    &amp;quot;.Trim();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In our tests, we can now query by anything. The returned content has the hierarchy and everything, so we can traverse it with &lt;code&gt;Children()&lt;/code&gt;, &lt;code&gt;Parent()&lt;/code&gt; and whatnot.
The only missing piece is the properties. Here's a test showing that we have everything but the title property of Page 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const int Page1Id = 1122;

[Test]
public void Returns_Empty_Documents()
{
    var contentCache = umbracoSupport.UmbracoContext.ContentCache;
    var page1 = contentCache.GetById(Page1Id);

    Assert.That(page1, Is
        .Not.Null
        .And
        .InstanceOf&amp;lt;PublishedContentWithKeyBase&amp;gt;()
        .And
        .Property(&amp;quot;Name&amp;quot;).EqualTo(&amp;quot;Page 1&amp;quot;)
        .And
        .Matches&amp;lt;IPublishedContent&amp;gt;(c =&amp;gt; c[&amp;quot;title&amp;quot;] == null)
        .And
        .Property(&amp;quot;Parent&amp;quot;)
            .Property(&amp;quot;Children&amp;quot;)
                .With.Count.EqualTo(2)
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Don't be discouraged though. This method is excellent for testing URL providers, ContentFinders, Menus, Sitemaps. You name it. I know I've written my fair share of hierarchy traversing code or fancy XPath queries. Unless of course, you need property values.&lt;/p&gt;
&lt;p&gt;Instead of pulling your leg about it, here's how we fix that.&lt;/p&gt;
&lt;h2&gt;Put some meat on the content&lt;/h2&gt;
&lt;p&gt;The reason the properties are not there isn't because they weren't read. It's because the &lt;code&gt;XmlPublishedContent&lt;/code&gt; that we get out ultimately relies on the &lt;code&gt;PublishedContentType&lt;/code&gt; for it's respective document type. Luckily, all Umbraco's services are already stubbed up for us, so we can give it what it needs.&lt;/p&gt;
&lt;p&gt;The gory guts of it is that it needs an &lt;code&gt;IContentType&lt;/code&gt; from the &lt;code&gt;ContentTypeService&lt;/code&gt;. We can easily stub one up with Moq: &lt;code&gt;var contentType = Mock.Of&amp;lt;IContentType&amp;gt;()&lt;/code&gt;. Further, it uses the &lt;code&gt;IContentType.CompositionPropertyTypes&lt;/code&gt; collection to iterate the properties. These &lt;code&gt;PropertyType&lt;/code&gt; instances are actually completely dependency-less, so we can just create some:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Mock.Get(contentType)
    .Setup(t =&amp;gt; t.CompositionPropertyTypes)
    .Returns(new[] {
        new PropertyType(&amp;quot;Umbraco.TinyMCEv3&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;body&amp;quot;)
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, we set it up on the &lt;code&gt;ContentTypeService&lt;/code&gt; stub:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Mock.Get(umbracoSupport.ServiceContext.ContentTypeService)
    .Setup(s =&amp;gt; s.GetContentType(alias))
    .Returns(contentType);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If only it were so easy. We depend on the &lt;code&gt;BaseWebTest&lt;/code&gt; class from &lt;code&gt;Umbraco.Tests&lt;/code&gt;. It sets up a content type factory that's being used somewhere in the hierarchy. It feeds &lt;code&gt;AutoPublishedContent&lt;/code&gt; instances instead of what we've stubbed up. We need to turn that off. There's a trick here. &lt;code&gt;UmbracoSupport&lt;/code&gt; should now live in an assembly called &lt;code&gt;Umbraco.UnitTests.Adapter&lt;/code&gt;. If you're pre 7.6.4 you need to go with &lt;code&gt;Umbraco.VisualStudio&lt;/code&gt;. This is because the factory we need to reset is internal to Umbraco. By having &lt;code&gt;UmbracoSupport&lt;/code&gt; in an assembly with one of these two names, we're able to do it. (Otherwise, you use reflection.) &lt;em&gt;By no means do this with production code. Just... forget it!&lt;/em&gt;&lt;br /&gt;
This paragraph should also get it's own blog post. :)&lt;/p&gt;
&lt;p&gt;But I digress. Here's the line you need to have the content use the &lt;code&gt;ContentTypeService&lt;/code&gt; to fetch its type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PublishedContentType.GetPublishedContentTypeCallback = null;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's tempting to leave setup code like this lying around in all our &lt;code&gt;SetUp&lt;/code&gt; methods or even in our &amp;quot;Arrange&amp;quot; sections. I've sinned too much, so those few lines are now part of &lt;code&gt;UmbracoSupport&lt;/code&gt; and can be used to set up multiple types for your fixture or test.&lt;/p&gt;
&lt;p&gt;Here's a test that fetches the same document as before, but can now read properties:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
public void With_DocumentTypes_Setup_Returns_Full_Blown_Documents()
{
    umbracoSupport.SetupContentType(&amp;quot;page&amp;quot;, new[]
    {
        new PropertyType(&amp;quot;textstring&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;title&amp;quot;),
        new PropertyType(&amp;quot;textarea&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;excerpt&amp;quot;),
        new PropertyType(&amp;quot;Umbraco.TinyMCEv3&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;body&amp;quot;),
        new PropertyType(&amp;quot;media&amp;quot;, DataTypeDatabaseType.Integer, &amp;quot;image&amp;quot;)
    });

    var page1 = contentCache.GetById(Page1Id);

    Assert.Multiple(() =&amp;gt;
    {
        Assert.That(page1[&amp;quot;title&amp;quot;], Is.EqualTo(&amp;quot;Welcome!&amp;quot;));
        Assert.That(page1[&amp;quot;excerpt&amp;quot;], Is.EqualTo(&amp;quot;Lorem ipsum dolor...&amp;quot;));
        Assert.That(page1[&amp;quot;body&amp;quot;].ToString(), Is.EqualTo(&amp;quot;&amp;lt;p&amp;gt;Lorem ipsum dolor...&amp;lt;/p&amp;gt;&amp;quot;));
        Assert.That(page1[&amp;quot;image&amp;quot;], Is.EqualTo(123));
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the .ToString() on the body. It's actually not a string, but some weird dynamic Umbraco thingy. I never saw that type before, but I didn't pursue it in time for this post. I don't want anything to do with it though, so let's storm on to the grand finale.&lt;/p&gt;
&lt;h2&gt;Let's make them strong already!&lt;/h2&gt;
&lt;p&gt;We're finally there. The last piece of the puzzle. Strongly typed content!&lt;/p&gt;
&lt;p&gt;It's managed by two resolvers: &lt;code&gt;PublishedContentModelFactoryResolver&lt;/code&gt; and &lt;code&gt;PropertyValueConvertersResolver&lt;/code&gt;. I won't go into details about those now, but suffice to say all resolvers have to be initialized before &lt;code&gt;BaseWebTest.Initialize&lt;/code&gt; and its ancestors.
I've added an &lt;code&gt;InitializeResolvers&lt;/code&gt; method to the &lt;code&gt;UmbracoSupport&lt;/code&gt; class where these two are initialized. The &lt;code&gt;PublishedContentModelFactoryResolver&lt;/code&gt; is set to a &lt;code&gt;FakeModelFactoryResolver&lt;/code&gt; that lets you register constructors for document type aliases. &lt;a href="https://github.com/lars-erik/umbraco-unit-testing-samples/blob/master/Umbraco.UnitTesting.Adapter/Support/FakeTypedModelFactory.cs"&gt;The code for this is available in my &amp;quot;Umbraco unit testing samples&amp;quot; repo on github&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;To set up property value converters, we also need to do that before registering the resolver. The resolver takes all the converters as constructor arguments. I've added a list of those types as a property on &lt;code&gt;UmbracoSupport&lt;/code&gt;, so we can add &lt;code&gt;IPropertyValueConverter&lt;/code&gt; implementing types before calling &lt;code&gt;UmbracoSupport.SetupUmbraco&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[SetUp]
public void Setup()
{
    umbracoSupport = new UmbracoSupport();

    // Converter types need to be added before setup
    umbracoSupport.ConverterTypes.Add(typeof(TinyMceValueConverter));

    umbracoSupport.SetupUmbraco();

    //...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To register the typed model, there's just one line you can do in your setup, or even in your tests. Here I've refactored out the setup for the content type from earlier, and I register a model type for the document type alias:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private void SetupContentType()
{
    umbracoSupport.SetupContentType(&amp;quot;page&amp;quot;, new[]
    {
        new PropertyType(&amp;quot;textstring&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;title&amp;quot;),
        new PropertyType(&amp;quot;textarea&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;excerpt&amp;quot;),
        new PropertyType(&amp;quot;Umbraco.TinyMCEv3&amp;quot;, DataTypeDatabaseType.Nvarchar, &amp;quot;body&amp;quot;),
        new PropertyType(&amp;quot;media&amp;quot;, DataTypeDatabaseType.Integer, &amp;quot;image&amp;quot;)
    });
}

[Test]
public void With_DocumentTypes_And_Models_Setup_Returns_Fully_Functional_Typed_Content()
{
    SetupContentType();

    // Register strongly typed models with the ModelFactory
    umbracoSupport.ModelFactory.Register(&amp;quot;page&amp;quot;, c =&amp;gt; new Page(c));

    var page1 = contentCache.GetById(Page1Id);

    Assert.That(page1, Is
        .InstanceOf&amp;lt;Page&amp;gt;()
        .And.Property(&amp;quot;Body&amp;quot;)
            .Matches&amp;lt;IHtmlString&amp;gt;(s =&amp;gt; 
                s.ToString() == &amp;quot;&amp;lt;p&amp;gt;Lorem ipsum dolor...&amp;lt;/p&amp;gt;&amp;quot;
            )
    );
}

public class Page : PublishedContentModel
{
    public Page(IPublishedContent content) : base((IPublishedContentWithKey)content)
    {
    }

    public string Title =&amp;gt; Content.GetPropertyValue&amp;lt;string&amp;gt;(&amp;quot;title&amp;quot;);
    public string Excerpt =&amp;gt; Content.GetPropertyValue&amp;lt;string&amp;gt;(&amp;quot;excerpt&amp;quot;);
    public IHtmlString Body =&amp;gt; Content.GetPropertyValue&amp;lt;IHtmlString&amp;gt;(&amp;quot;body&amp;quot;);
    public int Image =&amp;gt; Content.GetPropertyValue&amp;lt;int&amp;gt;(&amp;quot;image&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There you go! There's nothing more to it. Well, there is...&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Page&lt;/code&gt; class here is bundled with the test. If we use a common interface both for our runtime model and our test model, we're safe. But we should really use the runtime models. This means you shouldn't use &lt;em&gt;runtime generated&lt;/em&gt; models. &lt;a href="https://github.com/zpqrtbnk/Zbu.ModelsBuilder/wiki/Install-And-Configure"&gt;Go through the instructions for ModelsBuilder&lt;/a&gt; to have your models compiled and accessible from the tests.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;And although the XML is pretty ugly, you can flush it out into files bundled with your tests. You can also exploit the umbraco.config file and just copy segments from there into your test source files. That way, you spend no time writing the stubs, and the content is cleanly separated from your tests.&lt;/p&gt;
&lt;p&gt;That's &lt;em&gt;really&lt;/em&gt; all there is to it! It is. Now go test a bit, or a byte, or a string, or even a &lt;a href="/archive/testing-views-with-razorgenerator/"&gt;view&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lars-erik/umbraco-unit-testing-samples/tree/master/Umbraco.UnitTesting.Adapter/Support"&gt;The new version of UmbracoSupport including the fake model factory is available here.&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Tue, 17 Oct 2017 21:18:06 Z</pubDate>
      <a10:updated>2017-10-17T21:18:06Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1126</guid>
      <link>https://blog.aabech.no/archive/testing-views-with-razorgenerator/</link>
      <category>unit testing</category>
      <title>Testing views with RazorGenerator</title>
      <description>&lt;h2&gt;Razor what?&lt;/h2&gt;
&lt;p&gt;RazorGenerator is a hidden gem that lets you generate and pre-compile what would otherwise be generated and compiled at runtime. Your Razor. Not only does it give you a startup time boost, but it lets you unit test your views. The latter is the focus of this post.&lt;/p&gt;
&lt;p&gt;We'll continue to build on the project I described in &lt;a href="/archive/generating-documentation-from-nunit-tests/"&gt;my post on generating documentation with NUnit&lt;/a&gt;. It's a simple use case where an imaginary CMS has a feature to display events from a third party site. Here are the tests I've got so far:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1024/nunitoutput.png" alt="NUnit output" /&gt;&lt;/p&gt;
&lt;p&gt;In my previous post, I left the rendering test inconclusive. I like keeping inconclusive tests around as reminders of stuff I've got left to do. Let's have a quick look at the passing code before we dive into the rendering bits.&lt;/p&gt;
&lt;h2&gt;Basic HTTP integration and conversion tests&lt;/h2&gt;
&lt;p&gt;The first large piece of what's in there for now is a way to remove the physical dependency on the third party site. I like to stub away IO as far out as I can so I can test as much of my code quickly, yet as integrated as possible. In other words, as many participating classes as possible. Since we're making an example here, I'm just using the &lt;code&gt;HttpClient&lt;/code&gt; directly from the controller. The &lt;code&gt;HttpClient&lt;/code&gt; is hard to mock or fake, but it has an inner dependency that we can pass as an argument to a constructor: &lt;code&gt;HttpMessageHandler&lt;/code&gt;. It has the innermost function that the &lt;code&gt;HttpClient&lt;/code&gt; uses for any operation. It also has the rare trait of being &lt;code&gt;protected virtual&lt;/code&gt;, so we can stub it out. For this example, I'm just using a fake one that records requests and returns responses for registered URLs. Here it is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class FakeMessageHandler : HttpMessageHandler
{
    private readonly Dictionary&amp;lt;string, string&amp;gt; responses = new Dictionary&amp;lt;string,string&amp;gt;();
    public List&amp;lt;string&amp;gt; Calls { get; } = new List&amp;lt;string&amp;gt;();

    public void Register(string url, string response)
    {
        responses.Add(url, response);
    }

    protected override Task&amp;lt;HttpResponseMessage&amp;gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var url = request.RequestUri.ToString();

        Calls.Add(url);

        if (!responses.ContainsKey(url))
        { 
            return Task.FromResult(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.NotFound,
                Content = new StringContent(&amp;quot;{}&amp;quot;)
            });
        }

        return Task.FromResult(new HttpResponseMessage
        {
            Content = new StringContent(responses[url])
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We'll not dwell further on this one in this post, but you'll need it to run the following examples if you want to tag along.&lt;/p&gt;
&lt;p&gt;The tests that verifies that we call the right URL on the third party site is pretty simple. It checks the Calls collection on the FakeMessageHandler. Here's the test and the setup code needed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
public class When_Displaying_An_Event
{
    [Test]
    [DocumentationOrder(0)]
    [Description(@&amp;quot;
    Events are provided at eventsite with a REST api at the URL:
    https://eventsite/api/{id}
    &amp;quot;)]
    public void It_Is_Fetched_By_Id_From_The_Event_Server()
    {
        eventController.Event(234).Wait();

        Assert.AreEqual(
            &amp;quot;https://eventsite/api/234&amp;quot;, 
            httpMessageHandler.Calls.Single()
        );
    }

    // ... omitted other tests

    [SetUp]
    public void Setup()
    {
        httpMessageHandler = new FakeMessageHandler();
        httpClient = new HttpClient(httpMessageHandler);
        eventController = new EventController(httpClient);
    }

    private FakeMessageHandler httpMessageHandler;
    private HttpClient httpClient;
    private EventController eventController;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the controller:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class EventController : Controller
{
    private readonly HttpClient httpClient;

    public EventController(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public async Task&amp;lt;PartialViewResult&amp;gt; Event(int id)
    {
        var requestUri = &amp;quot;https://eventsite/api/&amp;quot; + id;
        var result = await httpClient.GetAsync(requestUri);
        var response = await result.Content.ReadAsStringAsync();
        // ... omitted further processing for now
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We pass the &lt;code&gt;FakeMessageHandler&lt;/code&gt; instance to a new &lt;code&gt;HttpClient&lt;/code&gt; effectively making it a stub. We can control the response for individual URLs, and verify that the right ones were called.&lt;/p&gt;
&lt;p&gt;Next step is to convert it to a valid model we can use in our view. We've got a second test asserting that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
[DocumentationOrder(1)]
[Description(&amp;quot;The JSON response from the event server is deserialized as the Event type.&amp;quot;)]
public void It_Is_Converted_To_An_Event_Model()
{
    var result = GetConcertEvent();
    var model = (Event)result.Model;

    Assert.AreEqual(&amp;quot;Concert of your life&amp;quot;, model.Name);
    Assert.AreEqual(new DateTime(2049,12,31,23,59,59), model.Time);
    Assert.AreEqual(&amp;quot;https://eventsite/123&amp;quot;, model.Url);
}

private PartialViewResult GetConcertEvent()
{
    httpMessageHandler.Register(
        &amp;quot;https://eventsite/api/123&amp;quot;,
        &amp;quot;{\&amp;quot;name\&amp;quot;:\&amp;quot;Concert of your life\&amp;quot;, \&amp;quot;time\&amp;quot;:2524607999}&amp;quot;
    );

    var result = eventController.Event(123).Result;
    return result;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here's the rest of the controller code creating that model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;PartialViewResult&amp;gt; Event(int id)
{
    var requestUri = &amp;quot;https://eventsite/api/&amp;quot; + id;
    var result = await httpClient.GetAsync(requestUri);
    var response = await result.Content.ReadAsStringAsync();
    var eventJObj = JsonConvert.DeserializeObject&amp;lt;JObject&amp;gt;(response);
    var evt = new Event
    {
        Name = eventJObj.Value&amp;lt;string&amp;gt;(&amp;quot;name&amp;quot;),
        Time = DateTimeOffset.FromUnixTimeSeconds(eventJObj.Value&amp;lt;long&amp;gt;(&amp;quot;time&amp;quot;)).DateTime,
        Url = &amp;quot;https://eventsite/&amp;quot; + id
    };
    return PartialView(&amp;quot;Event&amp;quot;, evt);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The test is a tedious verification of every property on the event object. There are several ways to get around that. Amongst others, equality members. I've got a way better trick, but that's for an upcoming post.&lt;/p&gt;
&lt;p&gt;Now that we're through those tests, let's dive into how we can test the actual HTML output of this whole shenanigan.&lt;/p&gt;
&lt;h2&gt;Generating some Razor&lt;/h2&gt;
&lt;p&gt;As mentioned initially, RazorGenerator is a hidden gem in the ASP.NET OSS wilderness. There's a Visual Studio plugin that you need to exploit it fully. It's aptly called RazorGenerator and &lt;a href="https://marketplace.visualstudio.com/items?itemName=DavidEbbo.RazorGenerator"&gt;can be installed from here&lt;/a&gt;. There's also a couple of Nuget packages that we want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/RazorGenerator.Mvc/"&gt;RazorGenerator.Mvc&lt;/a&gt;&lt;br /&gt;
Referenced in your website to make use of a special &lt;code&gt;ViewEngine&lt;/code&gt; for pre-compiled views.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/RazorGenerator.Testing/"&gt;RazorGenerator.Testing&lt;/a&gt;&lt;br /&gt;
Referenced in our test projects to be able to automagically render views in tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Armed with these tools, there's nothing left to do but add a custom tool to our view:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1029/razorgenerator_tool.png" alt="RazorGenerator as Custom Tool for a view" /&gt;&lt;/p&gt;
&lt;p&gt;As soon as you've blured the &amp;quot;Custom Tool&amp;quot; field, a .cs file will be generated beneath the view. Upon inspection it yields a class in the ASP namespace making a whole lot of Write* statements. This is what clutters up your ASP.NET Temporary files all the time. And the nice part: it can be instantiated. &lt;/p&gt;
&lt;p&gt;Here's the initial markup from Event.cshtml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@model Example.WebSite.Models.Event

&amp;lt;div&amp;gt;
    &amp;lt;a href=&amp;quot;@Model.Url&amp;quot;&amp;gt;
        &amp;lt;label&amp;gt;
            @Model.Name
        &amp;lt;/label&amp;gt;
        &amp;lt;span&amp;gt;
            @Model.Time.ToString(&amp;quot;f&amp;quot;)
        &amp;lt;/span&amp;gt;
    &amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Over in our unit test, we can now start adding some actual rendering of HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
[DocumentationOrder(2)]
public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
{
    var view = new _Views_Partials_Event_cshtml();
    var result = GetConcertEvent();

    Assert.AreEqual(&amp;quot;Event&amp;quot;, result.ViewName);

    var renderedResult = view.Render((Event) result.Model);

    Console.WriteLine(renderedResult);
    Assert.Inconclusive(&amp;quot;Rendering has not been implemented yet.&amp;quot;);
}

private PartialViewResult GetConcertEvent()
{
    httpMessageHandler.Register(
        &amp;quot;https://eventsite/api/123&amp;quot;,
        &amp;quot;{\&amp;quot;name\&amp;quot;:\&amp;quot;Concert of your life\&amp;quot;, \&amp;quot;time\&amp;quot;:2524607999}&amp;quot;
    );

    var result = eventController.Event(123).Result;
    return result;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I know, the view type name ain't too pretty. But it's necessary for the view engine to find the right one based on the path given. The cool part is the &lt;code&gt;Render&lt;/code&gt; statement. It's an extension method from the RazorGenerator.Tests package. It returns the final HTML as a string.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Console.WriteLine&lt;/code&gt; statement yields the following in our test output now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
    &amp;lt;a href=&amp;quot;https://eventsite/123&amp;quot;&amp;gt;
        &amp;lt;label&amp;gt;
            Concert of your life
        &amp;lt;/label&amp;gt;
        &amp;lt;span&amp;gt;
            fredag 31. desember 2049 23.59
        &amp;lt;/span&amp;gt;
    &amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;(Yes, I force my locale upon thee!)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What we've just done is to test our system almost end to end, cut off at the uttermost borders to external IO. Specifically, the third party site and the end-user's browser. &lt;/p&gt;
&lt;p&gt;Granted, we could do Selenium tests here to really test it via the browser, but my rule of thumb is that RazorGenerator is the best choice unless you've got a lot of JavaScript interactivity you need to test in integration. These are subjects for another post.&lt;/p&gt;
&lt;p&gt;There is a remaining issue though. We should assert the HTML we got rendered. We could store that HTML we got in a &lt;code&gt;const string expected&lt;/code&gt; in our test, but it's gonna foul up the code quite a bit. We could go with so-called &amp;quot;gold files&amp;quot; and implement something to compare a file to the actual output. There's a magical tool for that called ApprovalTests, which I'll cover in my next post. &lt;/p&gt;
&lt;p&gt;There's also the option of using HtmlAgilityPack to query the rendered view. The RazorGenerator.Tests package have built-in alternative for &lt;code&gt;Render&lt;/code&gt; called &lt;code&gt;RenderAsHtml&lt;/code&gt; that returns &lt;code&gt;HtmlDocument&lt;/code&gt; instances for you to query. It's quite useful when your only &lt;code&gt;Assert.That&lt;/code&gt; is for some specific element in a big page.&lt;/p&gt;
&lt;h2&gt;Resources and a small limitation&lt;/h2&gt;
&lt;p&gt;You've seen how you can use RazorGenerator to test your views. &lt;a href="http://blog.davidebbo.com/tag/#RazorGenerator"&gt;There are several posts by David Ebbo (one of the authors of RazorGenerator) on how to use RazorGenerator. Please check them out for more details than I was able to give here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For now it doesn't do nested &lt;code&gt;Html.Action&lt;/code&gt; or &lt;code&gt;Html.Partial&lt;/code&gt; calls. I've got a PR in the works, but I need to polish it for it to get in there. Some day soon. ;) If you really want to, you'll find my fork and build your own, but you'll be on your own.&lt;/p&gt;
&lt;p&gt;There's also a tiny performance hit. You'll have to wait a second longer for your tests to execute, since a lot of the ASP.NET MVC framework is spun up to render views. It's still less than the magical 2 second focus cap though, so you should be able to work effectively with it.&lt;/p&gt;
&lt;p&gt;I hope this piqued your interest in writing broader tests even up to the UI layers. There's even cooler tricks in store for you if you're already on .net Core, but the rest of us will have to make due until then.&lt;/p&gt;
</description>
      <pubDate>Mon, 09 Oct 2017 21:08:20 Z</pubDate>
      <a10:updated>2017-10-09T21:08:20Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1118</guid>
      <link>https://blog.aabech.no/archive/generating-documentation-from-nunit-tests/</link>
      <category>unit testing</category>
      <title>Generating documentation from NUnit tests</title>
      <description>&lt;h2&gt;What's the catch?&lt;/h2&gt;
&lt;p&gt;There's a lot of benefits with unit testing. One of the less exploited ones would be documentation.
I've heard it said, and I've said it myself countless times: Unit Tests can be used as documentation.
They are inherently documentation by being a good place to look for sample code.
But what if they could also serve as actual go-to documentation, a backlog, a technical specification and all those things that otherwise go rot in a Word document on some server?&lt;/p&gt;
&lt;p&gt;Lately, I've been experimenting with generating good documentation from NUnit tests for our ASP.NET MVC sites. (Based on Umbraco CMS, but that's not important to this article.)&lt;br /&gt;
I've been baffled by the lack of good information when I've googled for NUnit result transformations. So I gave it a go, and I'll share some techniques with you in this post.&lt;/p&gt;
&lt;p&gt;Many unit tests won't do for documentation though. I believe that testing should be done as integrated as possible, stopping just before they become slow. This allows for more user-centric, or holistic descriptions of the system. I'm doing my best to get better at naming them. They should read like stories describing the system. That way it'll be easy for other devs to find the right code to extend or debug. Namespacing and organization play a strong role, as well as good, descriptive names. It's hard, but I believe striving for this might actually also aid in the ability to use them as documentation. &lt;/p&gt;
&lt;h2&gt;An example&lt;/h2&gt;
&lt;p&gt;So for this example, let's have a look at a pretty simple case. We're integrating with a third-party event site to display relevant events on pages in our site. We have some kind of module that lets editors pick events and place them in pages. The part I'll use in this post is the part about fetching and displaying it.  
&lt;/p&gt;
&lt;p&gt;The tests I'm writing are feature specific, but technical enough in nature to belong in the realm of technical documentation. For business targeted documentation, you're better off looking into &lt;a href="https://cucumber.io/"&gt;BDD and all the practices that follows&lt;/a&gt;. If you don't have the team though, you might be able to get something working for you with the following techniques.&lt;/p&gt;
&lt;p&gt;Here's the test output I've got so far:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1024/nunitoutput.png" alt="NUnit output" /&gt;&lt;/p&gt;
&lt;p&gt;I think it reads fairly well. There shouldn't be too much doubt what this feature does. Granted, it's a really simple one, and complex systems will have thousands. Even more reason to group by feature, and add areas and other useful grouping levels as namespaces.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You'll also notice I've got a test that is inconclusive saying not implemented. I've started doing this to keep a kind of backlog of things to do. It's easy to forget about technical debt or rarer cases we've ignored. Having inconclusive tests glaring at you makes it easy to remember. It may also serve as a discussion point to bring up with the customer. &amp;quot;Should we bother to handle this weird case? It'll increase the budget with $...&amp;quot;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are still a couple of readability issues in the tests. When we add more features, it'd be nice to find all event-related tests in their own group. That's a quick fix. We'll add a namespace. (Put common tests in the same folder)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1025/nunitoutput_with_namespace.png" alt="NUnit output with namespace" /&gt;&lt;/p&gt;
&lt;p&gt;There's also a matter of order. Obviously fetching from server happens before converting to a model and then rendering it. There are concepts like ordered tests, but I believe atomic tests are faster and more flexible. I've got a solution for that, but let's live with it for now.&lt;/p&gt;
&lt;p&gt;Having this output from the testrunner or on the build server, even in your inbox when someone commits, is useful. We have a broad overview of what the system does. The tests names are decoupled from any implementation details so we're free to change the architecture while keeping our descriptive tests.&lt;/p&gt;
&lt;p&gt;But we could do more.&lt;/p&gt;
&lt;h2&gt;NUnit console runner output&lt;/h2&gt;
&lt;p&gt;When working on code, we use built-in test runners in our IDEs. We're able to run one or a few test in isolation and focus on just the right things. Build servers use command line runners and ususally produce some form of textual or HTML output. We can exploit the CLI tools as well. The NUnit console runner outputs an XML format by default. You can even do it from the package manager console in Visual Studio like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PM&amp;gt; .\packages\NUnit.ConsoleRunner.3.7.0\tools\nunit3-console.exe .\NUnitDocs.Tests\bin\debug\NUnitDocs.Tests.dll
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;If you're using Nuget to organize your dependencies, you can install the NUnit.ConsoleRunner package to get the executable in the packages folder. Otherwise, you can download it from the &lt;a href="http://nunit.org/download/"&gt;NUnit downloads page&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In addition to logging the results to the output, NUnit will write some metadata, and the following (more interesting) to TestOutput.xml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;test-run id=&amp;quot;2&amp;quot; testcasecount=&amp;quot;3&amp;quot; result=&amp;quot;Passed&amp;quot; total=&amp;quot;3&amp;quot; passed=&amp;quot;2&amp;quot; failed=&amp;quot;0&amp;quot; inconclusive=&amp;quot;1&amp;quot; &amp;gt;

  &amp;lt;!-- Attributes and metadata elements stripped for clarity --&amp;gt;

  &amp;lt;test-suite type=&amp;quot;TestSuite&amp;quot; id=&amp;quot;0-1005&amp;quot; name=&amp;quot;NUnitDocs&amp;quot; fullname=&amp;quot;NUnitDocs&amp;quot; runstate=&amp;quot;Runnable&amp;quot; testcasecount=&amp;quot;3&amp;quot; result=&amp;quot;Passed&amp;quot; total=&amp;quot;3&amp;quot; passed=&amp;quot;2&amp;quot; failed=&amp;quot;0&amp;quot; warnings=&amp;quot;0&amp;quot; inconclusive=&amp;quot;1&amp;quot;&amp;gt;
    &amp;lt;test-suite type=&amp;quot;TestSuite&amp;quot; id=&amp;quot;0-1006&amp;quot; name=&amp;quot;Tests&amp;quot; fullname=&amp;quot;NUnitDocs.Tests&amp;quot;&amp;gt;
      &amp;lt;test-suite type=&amp;quot;TestSuite&amp;quot; id=&amp;quot;0-1007&amp;quot; name=&amp;quot;Events&amp;quot; fullname=&amp;quot;NUnitDocs.Tests.Events&amp;quot;&amp;gt;
        &amp;lt;test-suite type=&amp;quot;TestFixture&amp;quot; id=&amp;quot;0-1000&amp;quot; name=&amp;quot;When_Displaying_An_Event&amp;quot; fullname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event&amp;quot;&amp;gt;
          &amp;lt;test-case id=&amp;quot;0-1002&amp;quot; name=&amp;quot;It_Is_Converted_To_An_Event_Model&amp;quot; fullname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event.It_Is_Converted_To_An_Event_Model&amp;quot; methodname=&amp;quot;It_Is_Converted_To_An_Event_Model&amp;quot; classname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event&amp;quot; runstate=&amp;quot;Runnable&amp;quot; seed=&amp;quot;1189788506&amp;quot; result=&amp;quot;Passed&amp;quot; start-time=&amp;quot;2017-10-08 19:44:11Z&amp;quot; end-time=&amp;quot;2017-10-08 19:44:12Z&amp;quot; duration=&amp;quot;0.413085&amp;quot; asserts=&amp;quot;3&amp;quot; /&amp;gt;
          &amp;lt;test-case id=&amp;quot;0-1001&amp;quot; name=&amp;quot;It_Is_Fetched_By_Id_From_The_Event_Server&amp;quot; fullname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event.It_Is_Fetched_By_Id_From_The_Event_Server&amp;quot; methodname=&amp;quot;It_Is_Fetched_By_Id_From_The_Event_Server&amp;quot; classname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event&amp;quot; runstate=&amp;quot;Runnable&amp;quot; seed=&amp;quot;486335788&amp;quot; result=&amp;quot;Passed&amp;quot; start-time=&amp;quot;2017-10-08 19:44:12Z&amp;quot; end-time=&amp;quot;2017-10-08 19:44:12Z&amp;quot; duration=&amp;quot;0.001595&amp;quot; asserts=&amp;quot;1&amp;quot; /&amp;gt;
          &amp;lt;test-case id=&amp;quot;0-1003&amp;quot; name=&amp;quot;It_Is_Rendered_With_A_Name_Date_And_A_Link&amp;quot; fullname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event.It_Is_Rendered_With_A_Name_Date_And_A_Link&amp;quot; methodname=&amp;quot;It_Is_Rendered_With_A_Name_Date_And_A_Link&amp;quot; classname=&amp;quot;NUnitDocs.Tests.Events.When_Displaying_An_Event&amp;quot; runstate=&amp;quot;Runnable&amp;quot; seed=&amp;quot;804806648&amp;quot; result=&amp;quot;Inconclusive&amp;quot; start-time=&amp;quot;2017-10-08 19:44:12Z&amp;quot; end-time=&amp;quot;2017-10-08 19:44:12Z&amp;quot; duration=&amp;quot;0.015905&amp;quot; asserts=&amp;quot;0&amp;quot;&amp;gt;
            &amp;lt;reason&amp;gt;
              &amp;lt;message&amp;gt;
                &amp;lt;![CDATA[Rendering has not been implemented yet.]]/&amp;gt;
              &amp;lt;/message&amp;gt;
            &amp;lt;/reason&amp;gt;
          &amp;lt;/test-case&amp;gt;
        &amp;lt;/test-suite&amp;gt;
      &amp;lt;/test-suite&amp;gt;
    &amp;lt;/test-suite&amp;gt;
  &amp;lt;/test-suite&amp;gt;
&amp;lt;/test-run&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I know, I know. It's not cool anymore. I don't like it any more than you, but there is one durable ancient technology we could use to do something with this output. Did you guess it yet?&lt;/p&gt;
&lt;h2&gt;XSLT!&lt;/h2&gt;
&lt;p&gt;There. I said it.&lt;/p&gt;
&lt;p&gt;Now let's look at a simple output before we dive into the more beefy things:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1026/transformed_output.png" alt="Transformed output" /&gt;&lt;/p&gt;
&lt;p&gt;Now ain't that starting to look readable? There's not that much to it. The NUnit console runner has a built-in parameter with the syntax --result=SPEC, where SPEC can point to an XSLT sheet. Here's the command to generate the former:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.\packages\NUnit.ConsoleRunner.3.7.0\tools\nunit3-console.exe .\NUnitDocs.Tests\bin\debug\NUnitDocs.Tests.dll  --result:&amp;quot;TestSummary.htm;transform=TestSummary.xslt&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The XSLT isn't much either. There's Bootstrap and (our trusty) JQuery from CDN. Some simple HTML formatting. And I wrote a one-liner JavaScript function to strip out all the underscores as well. Here's the first pass:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;iso-8859-1&amp;quot;?&amp;gt;
&amp;lt;xsl:stylesheet
    version=&amp;quot;1.0&amp;quot;
    exclude-result-prefixes=&amp;quot;msxsl&amp;quot;
    xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;
    xmlns:msxsl=&amp;quot;urn:schemas-microsoft-com:xslt&amp;quot;
    &amp;gt;
  &amp;lt;xsl:output method=&amp;quot;html&amp;quot; indent=&amp;quot;yes&amp;quot;/&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;@* | node()&amp;quot;&amp;gt;
    &amp;lt;html&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&amp;quot; integrity=&amp;quot;sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;/&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css&amp;quot; integrity=&amp;quot;sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;/&amp;gt;
        &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
          .Passed { color: green; }
          .Inconclusive { color: #BBAA00; }
          .Failed { color: red; }

          ul {
          margin-left: 0px;
          list-style-type: none;
          padding-left: 0;
          }

          ul ul {
          margin-left: 15px;
          }

          .counts {
          font-size: .7em;
          color: gray;
          }
        &amp;lt;/style&amp;gt;

      &amp;lt;/head&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
          &amp;lt;xsl:apply-templates select=&amp;quot;/test-run/test-suite&amp;quot;/&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;script src=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js&amp;quot; integrity=&amp;quot;sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;// Force closing tag&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;https://code.jquery.com/jquery-3.2.1.slim.min.js&amp;quot;&amp;gt;// Force closing tag&amp;lt;/script&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
          $(&amp;quot;td&amp;quot;).each(function(i, e) {
          $(e).text($(e).text().replace(/_/g, &amp;quot; &amp;quot;));
          });
        &amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;/test-run/test-suite&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;
      &amp;lt;xsl:value-of select=&amp;quot;./test-run/@name&amp;quot;/&amp;gt;
    &amp;lt;/h1&amp;gt;
    &amp;lt;table class=&amp;quot;table table-striped&amp;quot;&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;./test-suite&amp;quot;/&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;test-suite&amp;quot;&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;class&amp;quot;&amp;gt;
          &amp;lt;xsl:choose&amp;gt;
            &amp;lt;xsl:when test=&amp;quot;./@failed &amp;gt; 0&amp;quot;&amp;gt;Failed&amp;lt;/xsl:when&amp;gt;
            &amp;lt;xsl:when test=&amp;quot;./@inconclusive &amp;gt; 0&amp;quot;&amp;gt;Inconclusive&amp;lt;/xsl:when&amp;gt;
            &amp;lt;xsl:otherwise&amp;gt;Passed&amp;lt;/xsl:otherwise&amp;gt;
          &amp;lt;/xsl:choose&amp;gt;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;style&amp;quot;&amp;gt;
          padding-left: &amp;lt;xsl:value-of select=&amp;quot;count(ancestor::test-suite)*15&amp;quot;/&amp;gt;px;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:value-of select=&amp;quot;./@name&amp;quot;/&amp;gt;
      &amp;lt;/td&amp;gt;
      &amp;lt;td class=&amp;quot;counts&amp;quot;&amp;gt;
        &amp;lt;xsl:value-of select=&amp;quot;./@passed&amp;quot;/&amp;gt; passed,
        &amp;lt;xsl:value-of select=&amp;quot;./@inconclusive&amp;quot;/&amp;gt; inconclusive,
        &amp;lt;xsl:value-of select=&amp;quot;./@failed&amp;quot;/&amp;gt; failed
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;xsl:for-each select=&amp;quot;./test-suite&amp;quot;&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;.&amp;quot;/&amp;gt;
    &amp;lt;/xsl:for-each&amp;gt;
    &amp;lt;xsl:for-each select=&amp;quot;./test-case&amp;quot;&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;.&amp;quot;/&amp;gt;
    &amp;lt;/xsl:for-each&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;test-case&amp;quot;&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td colspan=&amp;quot;2&amp;quot;&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;style&amp;quot;&amp;gt;
          padding-left: &amp;lt;xsl:value-of select=&amp;quot;count(ancestor::test-suite)*15&amp;quot;/&amp;gt;px;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;class&amp;quot;&amp;gt;
          &amp;lt;xsl:value-of select=&amp;quot;./@result&amp;quot;/&amp;gt;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:value-of select=&amp;quot;./@name&amp;quot;/&amp;gt;
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;

  &amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is fairly good as a starting point, but we're stuck with the brief test names. Having some free text, at least at feature level or use-case level would be nice. And we still have this ordering problem to deal with. NUnit has the perfect tool for this: &lt;/p&gt;
&lt;h2&gt;NUnit PropertyAttribute&lt;/h2&gt;
&lt;p&gt;NUnit comes with a &lt;code&gt;PropertyAttribute&lt;/code&gt; that we can use to decorate our tests with metadata. It's main purpose is to add properties to fixtures and tests in the test output. It's an abstract class we can use to create our own metadata, and there's a few built-in like &lt;code&gt;DescriptionAttribute&lt;/code&gt;. There are uses of properties at runtime too, but that's way too advanced for this post.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;DescriptionAttribute&lt;/code&gt; is just what we want for the free text part of our documentation. It's added to the method just like the &lt;code&gt;Test&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
[Description(@&amp;quot;
Events are provided at eventsite with a REST api at the URL:
https://eventsite/api/{id}
&amp;quot;)]
public void It_Is_Fetched_By_Id_From_The_Event_Server()
{
    // ...
}

[Test]
[Description(&amp;quot;The JSON response from the event server is deserialized as the Event type.&amp;quot;)]
public void It_Is_Converted_To_An_Event_Model()
{
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;Description&lt;/code&gt; is written to the XML output like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;test-case id=&amp;quot;0-1002&amp;quot; name=&amp;quot;It_Is_Converted_To_An_Event_Model&amp;quot; ...&amp;gt;
    &amp;lt;properties&amp;gt;
        &amp;lt;property name=&amp;quot;Description&amp;quot; value=&amp;quot;The JSON response from the event server is deserialized as the Event type.&amp;quot; /&amp;gt;
    &amp;lt;/properties&amp;gt;
&amp;lt;/test-case&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By adding a few more voodoo lines to the XSLT and another one-liner JavaScript, we get the following output:&lt;br /&gt;
&lt;em&gt;(Don't worry, complete XSLT is available at the bottom fo the post.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1028/transformed_output_with_description.png" alt="Transformed HTML output with descriptions" /&gt;&lt;/p&gt;
&lt;p&gt;Now we're really getting somewhere. We can provide that little extra that tells the reader what's really happening and stuff that might be obscured by too many, or hopefully just the right amount of abstractions.&lt;/p&gt;
&lt;p&gt;Let's tackle the ordering problem while we're at it.&lt;/p&gt;
&lt;h2&gt;Implementing our own properties&lt;/h2&gt;
&lt;p&gt;The PropertyAttribute isn't much more than a metadata container. An ordering property is as simple as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class DocumentationOrderAttribute : PropertyAttribute
{
    public DocumentationOrderAttribute(int order)
        : base(&amp;quot;DocumentationOrder&amp;quot;, order)
    {
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's applied to the tests just like the descriptions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
[DocumentationOrder(0)]
[Description(@&amp;quot;
Events are provided at eventsite with a REST api at the URL:
https://eventsite/api/{id}
&amp;quot;)]
public void It_Is_Fetched_By_Id_From_The_Event_Server()
{
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And it's output just like descriptions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;test-case id=&amp;quot;0-1002&amp;quot; name=&amp;quot;It_Is_Converted_To_An_Event_Model&amp;quot; ...&amp;gt;
    &amp;lt;properties&amp;gt;
        &amp;lt;property name=&amp;quot;DocumentationOrder&amp;quot; value=&amp;quot;1&amp;quot; /&amp;gt;
        &amp;lt;property name=&amp;quot;Description&amp;quot; value=&amp;quot;The JSON response from the event server is deserialized as the Event type.&amp;quot; /&amp;gt;
    &amp;lt;/properties&amp;gt;
&amp;lt;/test-case&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can tackle the ordering problem with a simple XSLT sort element:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;xsl:for-each select=&amp;quot;./test-case&amp;quot;&amp;gt;
  &amp;lt;xsl:sort select=&amp;quot;./properties/property[@name='DocumentationOrder']/@value&amp;quot;/&amp;gt;
  &amp;lt;xsl:apply-templates select=&amp;quot;.&amp;quot;/&amp;gt;
&amp;lt;/xsl:for-each&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And we get a nice ordered output:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1027/transformed_output_ordered.png" alt="Transformed output ordered by attribute" /&gt;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;By structuring and naming our tests by feature we get nice headings for potential documentation. We also untie them completely from implementation so we're free to change our code. Applying a bit of metadata to our tests adds that little extra to make quite meaningful documentation.&lt;/p&gt;
&lt;p&gt;The fact that the results can be tranformed means we can create rich documentation UIs with search, tags, navigation structure and a bit less than just enough prose.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;We've still got that rendering test to pass. There's also a few too many asserts in one of the tests. In an upcoming post I'll share a couple of other tools with you and how to extend those to enrich the technical documentation even more.&lt;/p&gt;
&lt;h2&gt;Complete XSLT&lt;/h2&gt;
&lt;p&gt;Here's the complete XSLT for the examples above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;iso-8859-1&amp;quot;?&amp;gt;
&amp;lt;xsl:stylesheet
    version=&amp;quot;1.0&amp;quot;
    exclude-result-prefixes=&amp;quot;msxsl&amp;quot;
    xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;
    xmlns:msxsl=&amp;quot;urn:schemas-microsoft-com:xslt&amp;quot;
&amp;gt;
  &amp;lt;xsl:output method=&amp;quot;html&amp;quot; indent=&amp;quot;yes&amp;quot;/&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;@* | node()&amp;quot;&amp;gt;
    &amp;lt;html&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;!-- Latest compiled and minified CSS --&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&amp;quot; integrity=&amp;quot;sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;/&amp;gt;
        &amp;lt;!-- Optional theme --&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css&amp;quot; integrity=&amp;quot;sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;/&amp;gt;
        &amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
          .Passed { color: green; }
          .Inconclusive { color: #BBAA00; }
          .Failed { color: red; }

          ul {
          margin-left: 0px;
          list-style-type: none;
          padding-left: 0;
          }

          ul ul {
          margin-left: 15px;
          }

          label {
          font-weight: normal;
          }

          .counts {
          font-size: .7em;
          color: gray;
          }
        &amp;lt;/style&amp;gt;

      &amp;lt;/head&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
          &amp;lt;xsl:apply-templates select=&amp;quot;/test-run/test-suite&amp;quot;/&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;!-- Latest compiled and minified JavaScript --&amp;gt;
        &amp;lt;script src=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js&amp;quot; integrity=&amp;quot;sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa&amp;quot; crossorigin=&amp;quot;anonymous&amp;quot;&amp;gt;// Force closing tag&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;https://code.jquery.com/jquery-3.2.1.slim.min.js&amp;quot;&amp;gt;// Force closing tag&amp;lt;/script&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
          $(&amp;quot;td label&amp;quot;).each(function(i, e) {
          $(e).text($(e).text().replace(/_/g, &amp;quot; &amp;quot;));
          });
          $(&amp;quot;.description&amp;quot;).each(function(i, e) {
          $(e).html($(e).html().trim().replace(/\n/g, '&amp;lt;br/&amp;gt;'));
          });
        &amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;/test-run/test-suite&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;
      &amp;lt;xsl:value-of select=&amp;quot;./test-run/@name&amp;quot;/&amp;gt;
    &amp;lt;/h1&amp;gt;
    &amp;lt;table class=&amp;quot;table table-striped&amp;quot;&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;./test-suite&amp;quot;/&amp;gt;
    &amp;lt;/table&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;test-suite&amp;quot;&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;class&amp;quot;&amp;gt;
          &amp;lt;xsl:choose&amp;gt;
            &amp;lt;xsl:when test=&amp;quot;./@failed &amp;gt; 0&amp;quot;&amp;gt;Failed&amp;lt;/xsl:when&amp;gt;
            &amp;lt;xsl:when test=&amp;quot;./@inconclusive &amp;gt; 0&amp;quot;&amp;gt;Inconclusive&amp;lt;/xsl:when&amp;gt;
            &amp;lt;xsl:otherwise&amp;gt;Passed&amp;lt;/xsl:otherwise&amp;gt;
          &amp;lt;/xsl:choose&amp;gt;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;style&amp;quot;&amp;gt;
          padding-left: &amp;lt;xsl:value-of select=&amp;quot;count(ancestor::test-suite)*15&amp;quot;/&amp;gt;px;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;xsl:value-of select=&amp;quot;./@name&amp;quot;/&amp;gt;
      &amp;lt;/td&amp;gt;
      &amp;lt;td class=&amp;quot;counts&amp;quot;&amp;gt;
        &amp;lt;xsl:value-of select=&amp;quot;./@passed&amp;quot;/&amp;gt; passed,
        &amp;lt;xsl:value-of select=&amp;quot;./@inconclusive&amp;quot;/&amp;gt; inconclusive,
        &amp;lt;xsl:value-of select=&amp;quot;./@failed&amp;quot;/&amp;gt; failed
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;xsl:for-each select=&amp;quot;./test-suite&amp;quot;&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;.&amp;quot;/&amp;gt;
    &amp;lt;/xsl:for-each&amp;gt;
    &amp;lt;xsl:for-each select=&amp;quot;./test-case&amp;quot;&amp;gt;
      &amp;lt;xsl:sort select=&amp;quot;./properties/property[@name='DocumentationOrder']/@value&amp;quot;/&amp;gt;
      &amp;lt;xsl:apply-templates select=&amp;quot;.&amp;quot;/&amp;gt;
    &amp;lt;/xsl:for-each&amp;gt;
  &amp;lt;/xsl:template&amp;gt;

  &amp;lt;xsl:template match=&amp;quot;test-case&amp;quot;&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td colspan=&amp;quot;2&amp;quot;&amp;gt;
        &amp;lt;xsl:attribute name=&amp;quot;style&amp;quot;&amp;gt;
          padding-left: &amp;lt;xsl:value-of select=&amp;quot;count(ancestor::test-suite)*15&amp;quot;/&amp;gt;px;
        &amp;lt;/xsl:attribute&amp;gt;
        &amp;lt;label&amp;gt;
          &amp;lt;xsl:attribute name=&amp;quot;class&amp;quot;&amp;gt;
            &amp;lt;xsl:value-of select=&amp;quot;./@result&amp;quot;/&amp;gt;
          &amp;lt;/xsl:attribute&amp;gt;
          &amp;lt;xsl:value-of select=&amp;quot;./@name&amp;quot;/&amp;gt;
        &amp;lt;/label&amp;gt;
        &amp;lt;xsl:if test=&amp;quot;./properties/property[@name='Description']&amp;quot;&amp;gt;
          &amp;lt;div class=&amp;quot;description&amp;quot;&amp;gt;
            &amp;lt;xsl:value-of select=&amp;quot;./properties/property[@name='Description']/@value&amp;quot;/&amp;gt;
            &amp;lt;xsl:text&amp;gt; &amp;lt;/xsl:text&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/xsl:if&amp;gt;

      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;

  &amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Sun, 08 Oct 2017 21:06:26 Z</pubDate>
      <a10:updated>2017-10-08T21:06:26Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1117</guid>
      <link>https://blog.aabech.no/archive/potential-freeze-with-umbraco-and-signalr/</link>
      <title>Potential freeze with Umbraco and SignalR</title>
      <description>&lt;p&gt;I cloned the Umbraco 7.7 dev branch today to prepare a demo for my colleagues next week. SignalR has been bundled with Umbraco, and guess what - problems I've had before hit me again. It just freezes on boot, specifically at the app.MapSignalR call in the Owin startup.&lt;/p&gt;
&lt;p&gt;Turns out, the HQ guys have already been down this road and helped fix a really really weird bug with it. Read &lt;a href="https://github.com/SignalR/SignalR/issues/3414"&gt;the issue&lt;/a&gt; and &lt;a href="https://www.zpqrtbnk.net/posts/appdomains-threads-cultureinfos-and-paracetamol"&gt;Stephane's post&lt;/a&gt;, it's quite interesting. But this was not the issue that hit me, even tho it's quite similar in behavior.&lt;/p&gt;
&lt;p&gt;I've actually been down this road before myself. I recalled having had similar problems. I didn't dive into it back then, I just gave up and threw SignalR out. I'd like to say I have just as deep an analysis as Stephane has in his post, but I'm just not as good. :) In any case, it's something to do with the performance counters reading a registry key.&lt;/p&gt;
&lt;p&gt;The code is somewhere within &lt;code&gt;PerformanceCounter.NextSample()&lt;/code&gt;. My debugging symbols fu is not strong enough to get everything right there, but it's somewhere in the &lt;code&gt;PerformanceCounterLib&lt;/code&gt; and has something to do with a &lt;code&gt;RegistryKey&lt;/code&gt; being read. I had it in the stacktrace, but I'm sorry to say I didn't record it.&lt;/p&gt;
&lt;p&gt;Popping the registry editor, I find it already focused on a registry key called SignalR. I'm guessing I haven't had it open since I gave up the last time. Again, sorry for not having deeper knowledge here. You might have a gazillion keys called SignalR-something.vstemplate. These are safe, but the one only named SignalR is our suspect. I impatiently went ahead and deleted it, and now everything works.&lt;/p&gt;
&lt;p&gt;So I'm stuck trying to provide any deeper knowledge, and I was stupid enough not to record the key name before I threw it out. Although, I'm pretty sure it is from the &lt;a href="http://www.asp.net/signalr/overview/performance/signalr-performance#perfcounters"&gt;SignalR performance counters&lt;/a&gt;, and that the key in question is &lt;code&gt;HKLM\System\CurrentControlSet\Services\SignalR\Performance&lt;/code&gt;. I threw the whole &lt;code&gt;SignalR&lt;/code&gt; key out.&lt;/p&gt;
&lt;p&gt;If I gain any deeper knowledge, I'll update this post. Otherwise, at least you know that deleting that key may save your day. :)&lt;/p&gt;
</description>
      <pubDate>Fri, 23 Jun 2017 22:16:38 Z</pubDate>
      <a10:updated>2017-06-23T22:16:38Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1116</guid>
      <link>https://blog.aabech.no/archive/copying-umbraco-datatype-settings-prevalues/</link>
      <title>Copying Umbraco datatype settings (prevalues).</title>
      <description>&lt;p&gt;I had to copy a grid with settings and our &amp;quot;default template&amp;quot; settings today. Given there's no copy datatype feature in Umbraco yet, I had the option of ticking all the boxes manually, or invent a quicker way. So I ended up just doing a quick hack in SQL to do the work for me. I'm mostly putting it here for my own reference for later, but maybe - just maybe it's useful for you too. :)&lt;/p&gt;
&lt;p&gt;If you create a new datatype with the same property editor in the backoffice, you can compare it's settings with an existing one as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select
    *
FROM 
    cmsDataTypePreValues target
    inner join cmsDataTypePreValues source  on 
        source.datatypenodeId = 1048 and target.datatypenodeid = 1630
        and
        source.alias = target.alias
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just swap out 1038 and 1630 with your respective source values.
If you run the query you'll see that we'd like the values on the right to be copied to the left ones. That's just a matter of running and update. Wrap it in a transaction just to check that you don't update 1000 prevalues inadvertently:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;begin tran
go

update target set 
    target.value = source.value
FROM 
    cmsDataTypePreValues target
    inner join cmsDataTypePreValues source  on 
        source.datatypenodeId = 1048 and target.datatypenodeid = 1630
        and
        source.alias = target.alias

go
rollback tran
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If it says it updated as many rows as you have settings, you're in the green and can swap the &lt;code&gt;rollback tran&lt;/code&gt; to a &lt;code&gt;commit tran&lt;/code&gt; and there you go. All settings copied.&lt;/p&gt;
&lt;p&gt;Now all we've got to do is wrap this in a WebAPI call and we've almost got the copy/paste datatypes feature in Umbraco. :)&lt;/p&gt;
</description>
      <pubDate>Tue, 18 Apr 2017 12:25:17 Z</pubDate>
      <a10:updated>2017-04-18T12:25:17Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1115</guid>
      <link>https://blog.aabech.no/archive/automating-creation-of-source-data-for-tests/</link>
      <title>Automating creation of source data for tests</title>
      <description>&lt;p&gt;I'm writing some tests for a synchronization job between two systems at the moment. Especially for these kinds of projects, but also most other, I need to have a fair amount of test-data to spin off useful tests.&lt;/p&gt;
&lt;p&gt;The worst way of doing this is building up huge object graphs in setup methods. I guess an even worse way is to set up big object graphs using mocks, setting up each property. I've seen and probably did both, but it basically just makes you stop testing if you can't find another way.&lt;/p&gt;
&lt;p&gt;So I've learned to move such test-data out into separate files. Often in a &amp;quot;TestData&amp;quot; folder or the like. Naming gets a bit random, but at least it's better.&lt;/p&gt;
&lt;p&gt;But after starting to use &lt;a href="http://approvaltests.com/"&gt;ApprovalTests&lt;/a&gt; by &lt;a href="https://twitter.com/LlewellynFalco"&gt;Llewellyn Falco&lt;/a&gt;, I got inspired by the way it magically adds received and approved files named after specific tests. It's quite genious. And the best part is that NUnit more or less supports everything you need to do so in other situations.&lt;/p&gt;
&lt;p&gt;Let's say I have this test and some test-data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
public class When_Updating
{
    [Test]
    public void Then_Posts_Data()
    {
        var arr = JsonConvert.DeserializeObject&amp;lt;JArray&amp;gt;(Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory,
            @&amp;quot;TestData\some-test-file.json&amp;quot;
         ));
        // ... test stuff
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I could extract this into a helper and pass &amp;quot;TestData\some-test-file.json&amp;quot; perhaps. But it could be even better. NUnit has this &lt;code&gt;TestContext&lt;/code&gt; singleton that holds the current fixture and test names. (Amongst other things.) We can use this to our advantage. Have a look at this helper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private static T ReadTestSource&amp;lt;T&amp;gt;()
    where T : new()
{
    var sourceFileName = Type.GetType(TestContext.CurrentContext.Test.ClassName)?.Name + &amp;quot;.&amp;quot; + TestContext.CurrentContext.Test.MethodName + &amp;quot;.Source.json&amp;quot;;
    var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @&amp;quot;..\..&amp;quot;, sourceFileName);
    if (!File.Exists(filePath))
        using (var file = File.CreateText(filePath))
            file.Write(JsonConvert.SerializeObject(new T()));
    return JsonConvert.DeserializeObject&amp;lt;T&amp;gt;(File.ReadAllText(filePath));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Given this, we can replace the entire test-data getter to a single parameterless (generic, tho) method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
public class When_Updating
{
    [Test]
    public void Then_Posts_Data()
    {
        var arr = ReadTestSource&amp;lt;JArray&amp;gt;();
        // ... test stuff
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If I did this in a completely fresh test, I would actually get a JSON file on disk, called &amp;quot;When_Updating.Then_Posts_Data.Source.json&amp;quot;. Since I passed &lt;code&gt;JArray&lt;/code&gt; as the generic parameter, the file will already contain &lt;code&gt;[]&lt;/code&gt;. If it were an object, it'd have all the keys. I can just start filling it out. In this case, I've got another slower integration test I can copy some data from, and I'll have my source data at hand.&lt;/p&gt;
&lt;p&gt;There's more magic that could be done, but I found these 9 lines of code so cool and powerful not to go ahead and share it at once. :) One important thing missing is finding the tests folder, but I'm sure you're up for that challenge if you find this useful.&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;[Edit]&lt;/strong&gt; I really, really needed that namespace/test-folder pretty soon, so here's a non-refactored method that does just that. :)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private static T ReadTestSource&amp;lt;T&amp;gt;()
    where T : new()
{
    var rootPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @&amp;quot;..\..&amp;quot;));
    var type = Type.GetType(TestContext.CurrentContext.Test.ClassName);
    var path = type?.Namespace?.Replace(type.Assembly.GetName().Name + &amp;quot;.&amp;quot;, &amp;quot;&amp;quot;).Replace(&amp;quot;.&amp;quot;, &amp;quot;\\&amp;quot;);
    var testFolder = Path.Combine(rootPath, path ?? &amp;quot;&amp;quot;);
    if (!Directory.Exists(testFolder))
        throw new Exception(&amp;quot;Need to keep namespace and folder in sync to create test-source file&amp;quot;);
    var sourceFileName = type?.Name + &amp;quot;.&amp;quot; + TestContext.CurrentContext.Test.MethodName + &amp;quot;.Source.json&amp;quot;;
    var filePath = Path.Combine(testFolder, sourceFileName);
    if (!File.Exists(filePath))
        using (var file = File.CreateText(filePath))
            file.Write(JsonConvert.SerializeObject(new T()));
    return JsonConvert.DeserializeObject&amp;lt;T&amp;gt;(File.ReadAllText(filePath));
}
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Thu, 06 Apr 2017 10:11:09 Z</pubDate>
      <a10:updated>2017-04-06T10:11:09Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1114</guid>
      <link>https://blog.aabech.no/archive/the-basics-of-unit-testing-umbraco-just-got-simpler/</link>
      <title>The basics of unit testing Umbraco just got simpler</title>
      <description>&lt;h2&gt;Status Quo of Umbraco Testing 2017&lt;/h2&gt;
&lt;p&gt;It's been a year since giving my &lt;a href="https://vimeo.com/channels/codegarden16/183623111"&gt;presentation about Unit Testing Umbraco at Codegarden 2016&lt;/a&gt;. Testing Umbraco is ever more popular, and I'm happy to see there's more people talking about it on twitter, at festivals and at gatherings. Recently, &lt;a href="https://twitter.com/c_thillemann"&gt;Christian Thillemann&lt;/a&gt; decided he was tired of waiting for the request I created last year about having Umbraco.Tests on Nuget. He went ahead and set up an AppVeyor script and we now have automatic releases of &lt;a href="https://www.nuget.org/packages/Our.Umbraco.Community.Tests/"&gt;Our.Umbraco.Community.Tests&lt;/a&gt; on nuget whenever there's a new Umbraco version out. #H5YR, @kedde! :)&lt;/p&gt;
&lt;p&gt;Of course, having this requirement and having to lean that heavily on an external assembly just to get to annoying internals in our core product is just an intermediate challenge. Come Umbraco 9 on .net Core, we'll (hopefully) be able to just spin up a complete hosted Umbraco site from our tests and inject / swap out whatever we'd fancy in that startup. Java and other platforms has had that option for years, so it's about time the .net community gets there. But enough ranting about that, let's just keep contributing so we get closer to running on .net Core.&lt;/p&gt;
&lt;h2&gt;Meanwhile...&lt;/h2&gt;
&lt;p&gt;In the mean time, I've been exploring further practices with automated testing. I'll get back to those in future posts and presentations. But while doing this, I stubled into an alternative practice to the one I've been preaching. It's mostly the same, but it challenges the inherital structure of Umbraco.Tests.&lt;/p&gt;
&lt;p&gt;A really good principle when you write tests is letting your tests be interfaces to your code. Whenever you write a user interface, you take good care putting your logic in separate classes. The UI stuff is just UI stuff. Maybe our tests should be like that too? When you do that, you can even re-use your tests for different layers of your code! I'll not go into details about that in this post, but a natural result was to offload everything about Umbraco into its own class. I don't know why I didn't before, or why it took me so long to do it, but I suppose it's got something to do with closed mentality. When you see a pattern (inherited unit-tests), you automatically think it's a good one, and don't challenge it.&lt;/p&gt;
&lt;h2&gt;An epiphany&lt;/h2&gt;
&lt;p&gt;If you've gone through my post about &lt;a href="/archive/the-basics-of-unit-testing-umbraco/"&gt;the basics of testing Umbraco&lt;/a&gt;, you know you can inherit quite a few different base tests from Umbraco.Tests. They all have their uses, and I still recommend using the &lt;code&gt;BaseDatabaseFactoryTest&lt;/code&gt; if you're testing stuff integrated with a database. However, most of what I write are different &lt;code&gt;Surface-&lt;/code&gt;, &lt;code&gt;Render-&lt;/code&gt; and &lt;code&gt;*ApiControllers&lt;/code&gt; using fairly few stubbable things. All the front-end ones have the same basic initial state needs, and the backoffice ones just have fewer. So we end up inheriting &lt;code&gt;BaseRoutingTest&lt;/code&gt; and calling &lt;code&gt;GetRoutingContext(...)&lt;/code&gt; in all our setups. Being the smart devs you are, I'm sure a lot of you also ended up with some kind of &lt;code&gt;MyUmbracoTestBase&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But there's something completely wrong with that!&lt;/strong&gt; We're letting the dependency on &lt;em&gt;Umbraco&lt;/em&gt; get in the way of our own automation code. We can't create a hirarchy of tests that isn't dependent on Umbraco. For instance, if we had a base class initializating our own domain in our core domain tests, we couldn't re-use that for our MVC tests. To do that we'd have to inherit our core base test class from Umbraco tests, and then Umbraco would leak into our core domain. We don't want that.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;SRP&lt;/a&gt; your tests of course! Excercise the same dicipline by applying nice layering to your test automation code as you do to your other code. I ended up refactoring most of my test classes' setups and teardowns into a &amp;quot;Support&amp;quot; class with only three public methods. Here's how you'd set up a test for a simple SurfaceController with this method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestFixture]
public class Adding_Numbers
{
    private UmbracoSupport support = new UmbracoSupport();

    [SetUp]
    public void Setup()
    {
        support.SetupUmbraco();
    }

    [TearDown]
    public void TearDown()
    {
        support.DisposeUmbraco();
    }

    [Test]
    public void Posting_AddModel_Calculates_Result()
    {
        const int expectedSum = 3;
        var controller = new SimpleSurfaceController();
        var result = (AdditionModel)controller.Add(1, 2);
        Assert.AreEqual(expectedSum, result.Model.Sum);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;I want to stress that this is not, and will not be a package. See below for reasons.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So it isn't that different from inheriting &lt;code&gt;BaseRoutingTest&lt;/code&gt;. The only difference is that we delegate the setup and teardown to another type instance instead of delegating it to the base class. The &lt;code&gt;UmbracoSupport&lt;/code&gt; class still derives from &lt;code&gt;BaseRoutingTest&lt;/code&gt;, but it stops there. It won't leak further.&lt;/p&gt;
&lt;p&gt;Note that NUnit creates an instance of your test fixture for each test, so you'll get a fresh UmbracoSupport instance for each of your tests.&lt;/p&gt;
&lt;p&gt;The third method you'd want to call is &lt;code&gt;PrepareController()&lt;/code&gt;. The moment you'd like to act upon the &lt;code&gt;CurrentPage&lt;/code&gt;, or just use &lt;code&gt;RedirectToCurrentUmbracoPage&lt;/code&gt;, you'll have to call &lt;code&gt;support.PrepareController(controller)&lt;/code&gt;. This wires up the Umbraco request context instance to your controller's state.&lt;/p&gt;
&lt;p&gt;When you've done that, you've got quite a few properties on the support class that let's you get to the juicy stuff you'll want to stub:&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Property&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UmbracoContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The current &lt;code&gt;UmbracoContext&lt;/code&gt;. Inject it to your controllers and whatnot&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ServiceContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The complete &lt;code&gt;IServiceContext&lt;/code&gt; with clean stubs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CurrentPage&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A mock of the currently served &lt;code&gt;IPublishedContent&lt;/code&gt;. Same as &lt;code&gt;UmbracoContext.Current.PublishedRequestContext.PublishedContent&lt;/code&gt;, &lt;code&gt;UmbracoHelper.AssignedContentItem&lt;/code&gt; and &lt;code&gt;SurfaceController.CurrentPage&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;RouteData&lt;/code&gt;&lt;/td&gt;&lt;td&gt;You might hit further usage of this internally to Umbraco. It's available for modification when you do&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;HttpContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The good ol' "testable" &lt;code&gt;HttpContextBase&lt;/code&gt; from MVC, created along with the &lt;code&gt;UmbracoContext&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;h2&gt;The implementation&lt;/h2&gt;
&lt;p&gt;I've already implemented this class in the &lt;a href="https://github.com/lars-erik/umbraco-unit-testing-samples"&gt;umbraco-unit-testing-samples repo&lt;/a&gt;. I'll post it here as well for you to copy uncritically. I won't go through the details. It's just a refactored version of &lt;a href="http://blog.aabech.no/archive/the-basics-of-unit-testing-umbraco/"&gt;what I've shown in previous posts&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Globalization;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;

namespace Umb.Testing.Tests.Support
{
    public class UmbracoSupport : BaseRoutingTest
    {
        public UmbracoContext UmbracoContext =&amp;gt; umbracoContext;

        public new ServiceContext ServiceContext =&amp;gt; serviceContext;

        public IPublishedContent CurrentPage =&amp;gt; currentPage;

        public RouteData RouteData =&amp;gt; routeData;

        public UmbracoHelper UmbracoHelper =&amp;gt; umbracoHelper;

        public HttpContextBase HttpContext =&amp;gt; umbracoContext.HttpContext;

        public string ContentCacheXml { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Initializes a stubbed Umbraco request context. Generally called from [SetUp] methods.
        /// Remember to call UmbracoSupport.DisposeUmbraco from your [TearDown].
        /// &amp;lt;/summary&amp;gt;
        public void SetupUmbraco()
        {
            InitializeFixture();
            TryBaseInitialize();

            InitializeSettings();

            CreateCurrentPage();
            CreateRouteData();
            CreateContexts();
            CreateHelper();

            InitializePublishedContentRequest();
        }

        /// &amp;lt;summary&amp;gt;
        /// Cleans up the stubbed Umbraco request context. Generally called from [TearDown] methods.
        /// Must be called before another UmbracoSupport.SetupUmbraco.
        /// &amp;lt;/summary&amp;gt;
        public void DisposeUmbraco()
        {
            TearDown();
        }

        /// &amp;lt;summary&amp;gt;
        /// Attaches the stubbed UmbracoContext et. al. to the Controller.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;controller&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
        public void PrepareController(Controller controller)
        {
            var controllerContext = new ControllerContext(HttpContext, RouteData, controller);
            controller.ControllerContext = controllerContext;

            routeData.Values.Add(&amp;quot;controller&amp;quot;, controller.GetType().Name.Replace(&amp;quot;Controller&amp;quot;, &amp;quot;&amp;quot;));
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;Dummy&amp;quot;);
        }

        protected override string GetXmlContent(int templateId)
        {
            if (ContentCacheXml != null)
                return ContentCacheXml;

            return base.GetXmlContent(templateId);
        }

        private UmbracoContext umbracoContext;
        private ServiceContext serviceContext;
        private IUmbracoSettingsSection settings;
        private RoutingContext routingContext;
        private IPublishedContent currentPage;
        private RouteData routeData;
        private UmbracoHelper umbracoHelper;
        private PublishedContentRequest publishedContentRequest;

        protected override ApplicationContext CreateApplicationContext()
        {
            // Overrides the base CreateApplicationContext to inject a completely stubbed servicecontext
            serviceContext = MockHelper.GetMockedServiceContext();
            var appContext = new ApplicationContext(
                new DatabaseContext(Mock.Of&amp;lt;IDatabaseFactory&amp;gt;(), Logger, SqlSyntax, GetDbProviderName()),
                serviceContext,
                CacheHelper,
                ProfilingLogger);
            return appContext;
        }

        private void TryBaseInitialize()
        {
            // Delegates to Umbraco.Tests initialization. Gives a nice hint about disposing the support class for each test.
            try
            {
                Initialize();
            }
            catch (InvalidOperationException ex)
            {
                if (ex.Message.StartsWith(&amp;quot;Resolution is frozen&amp;quot;))
                    throw new Exception(&amp;quot;Resolution is frozen. This is probably because UmbracoSupport.DisposeUmbraco wasn't called before another UmbracoSupport.SetupUmbraco call.&amp;quot;);
            }
        }

        private void InitializeSettings()
        {
            // Stub up all the settings in Umbraco so we don't need a big app.config file.
            settings = SettingsForTests.GenerateMockSettings();
            SettingsForTests.ConfigureSettings(settings);
        }

        private void CreateCurrentPage()
        {
            // Stubs up the content used as current page in all contexts
            currentPage = Mock.Of&amp;lt;IPublishedContent&amp;gt;();
        }

        private void CreateRouteData()
        {
            // Route data is used in many of the contexts, and might need more data throughout your tests.
            routeData = new RouteData();
        }

        private void CreateContexts()
        {
            // Surface- and RenderMvcControllers need a routing context to fint the current content.
            // Umbraco.Tests creates one and whips up the UmbracoContext in the process.
            routingContext = GetRoutingContext(&amp;quot;http://localhost&amp;quot;, -1, routeData, true, settings);
            umbracoContext = routingContext.UmbracoContext;
        }

        private void CreateHelper()
        {
            umbracoHelper = new UmbracoHelper(umbracoContext, currentPage);
        }

        private void InitializePublishedContentRequest()
        {
            // Some deep core methods fetch the published content request from routedata
            // others access it through the context
            // in any case, this is the one telling everyone which content is the current content.

            publishedContentRequest = new PublishedContentRequest(new Uri(&amp;quot;http://localhost&amp;quot;), routingContext, settings.WebRouting, s =&amp;gt; new string[0])
            {
                PublishedContent = currentPage,
                Culture = CultureInfo.CurrentCulture
            };

            umbracoContext.PublishedContentRequest = publishedContentRequest;

            var routeDefinition = new RouteDefinition
            {
                PublishedContentRequest = publishedContentRequest
            };

            routeData.DataTokens.Add(&amp;quot;umbraco-route-def&amp;quot;, routeDefinition);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Why no package?&lt;/h2&gt;
&lt;p&gt;You might be wondering why I didn't package this up into a nice reusable nuget package for y'all to install. It's because THIS ISN'T IT! It isn't a silver bullet. It's boilerplate. You're bound to want to add your own varying states and preconditions to this setup. If I'd packaged it, we'd need an &amp;quot;OnThis&amp;quot;, &amp;quot;OnThat&amp;quot; and &amp;quot;OnWhatever&amp;quot; for everything that happens so you could extend it. I've done several PRs to the core to have Umbraco.Tests usable as it is now, and I don't want to create another such layer.&lt;/p&gt;
&lt;p&gt;You might for instance have to fiddle a bit with &lt;code&gt;CreateApplicationContext()&lt;/code&gt;, and I'm quite sure that any sligtly advanced site will have to have tests setting up stuff in &lt;code&gt;FreezeResolution()&lt;/code&gt;. That'd be solvable with inheritance, but here we go again... ;)&lt;/p&gt;
&lt;h2&gt;Next up&lt;/h2&gt;
&lt;p&gt;There's a few doors that open when you extract everything out of your tests. I'm dying to show you more, but I'll have to leave a lame &amp;quot;to-be-continued&amp;quot; and hope I don't mess it up. Happy refactoring, and do go get that nuget package from @kedde!&lt;/p&gt;
</description>
      <pubDate>Sat, 18 Mar 2017 22:21:23 Z</pubDate>
      <a10:updated>2017-03-18T22:21:23Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1113</guid>
      <link>https://blog.aabech.no/archive/rather-package-manager-console-than-print/</link>
      <title>Rather Package Manager Console than Print?</title>
      <description>&lt;p&gt;I don't know about you, but I printed my last sheet of code sometime during the mid-nineties. Yet when you press Ctrl+P in Visual Studio, guess what happens? You may print your code! I don't think I ever did that. If I ever printed a class diagram, I must have used the File-&amp;gt;Print menu item anyway.&lt;/p&gt;
&lt;p&gt;What I do use often though, is the Nuget Package Manager Console. It bothered me that the console is purely for keyboard input, but you have to use the mouse to get to it. And Eureka! What better shortcut to get to the package manager console than Ctrl+P?&lt;/p&gt;
&lt;p&gt;I know Ctrl+P is the recommended shortcut for printing across Windows (Command+P on mac?), but come on! I've gone and added a feature request on &lt;a href="https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/18361723-set-ctrl-p-to-be-shortcut-for-the-package-manager"&gt;uservoice.visualstudio.com&lt;/a&gt;, though I guess the recommendation beats usefulness.&lt;/p&gt;
&lt;h2&gt;You can still do it&lt;/h2&gt;
&lt;p&gt;If you know your way around Visual Studio and think this is a good idea, I guess you've already customized your environment. If you don't know how to do it, here's how:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1023/visualstudiooptions.png" alt="Visual Studio keyboard options, setting shortcut for package manager console" /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the Tools menu and select Options.  
&lt;/li&gt;
&lt;li&gt;Navigate to Environment\Keyboard&lt;/li&gt;
&lt;li&gt;Under &amp;quot;Show commands containing&amp;quot;, search for &amp;quot;View.PackageManagerConsole&amp;quot;&lt;/li&gt;
&lt;li&gt;Under &amp;quot;Press shortcut keys&amp;quot;, hit Ctrl+P&lt;/li&gt;
&lt;li&gt;Click &amp;quot;Assign&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And there you go, you're ready to pop the package manager console with Ctrl+P.&lt;/p&gt;
</description>
      <pubDate>Fri, 17 Feb 2017 11:30:32 Z</pubDate>
      <a10:updated>2017-02-17T11:30:32Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1107</guid>
      <link>https://blog.aabech.no/archive/how-to-kill-umbraco-with-urlrewritenet/</link>
      <title>How to kill Umbraco with UrlRewrite.Net</title>
      <description>&lt;h3&gt;The case&lt;/h3&gt;
&lt;p&gt;Recently we were contacted by another Norwegian company who inherited a custom Umbraco solution. The original developer is off chasing green fields, so they came to us when the site restarted twice an hour. Something about CPU spikes... It had been upgraded to 7.5.4 and everything, so we could just start off clicking all the shiny new buttons in the health dashboard. Most &amp;quot;known&amp;quot; Azure fixes were already in place, and the rest was applied for good measure. Yet the problem persisted, so we knew we were facing a more dire problem.&lt;/p&gt;
&lt;h3&gt;The proof&lt;/h3&gt;
&lt;p&gt;Jumping onto the Azure graphs and logs, we could see a pattern of massive memory usage as well as the CPU. Here's a nice bouncy graph of the state of the S3(!) server.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1022/spikes.png" alt="Spikes in memory and CPU" /&gt;&lt;/p&gt;
&lt;p&gt;Going through the Umbraco logs, we found several restarts due to out of memory exceptions from seemingly unrelated calls. All of them seemed to correlate with the restarts and massive resource spikes. Some of them came from cached views, so we did an attemt to remove caching from the most varied pages. After all, Umbraco already has the content in memory, so how much CPU goes into rendering it? It was quickly dismissed though, the problem persisted.  
&lt;/p&gt;
&lt;p&gt;We'd also spotted another suspect in the logs, namely UrlRewrite.Net. But at this point we were also ready to throw some profiling tools on the site. Somehow, none of JetBrains' tools worked. For some reason the views could not be rendered. I suspect it has something to do with the caching, profiling views or how the tools work. But I digress. We were able to run VS' profiler on it, and guess who turned up as our main suspect again?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1020/cpuprofile.png" alt="Profiling UrlRewrite.Net with Umbraco" /&gt;&lt;/p&gt;
&lt;p&gt;I think the table speaks for it self. The upper two calls are in idling background threads.
They don't affect CPU at all. Having the system access the database for one second is within limits for several requests. But the rest of the CPU time was actually spent solely in UrlRewrite.Net and parsing of regular expressions. I could quickly verify we weren't the first seeing this after searching &lt;a href="http://our.umbraco.org"&gt;our.umbraco.org&lt;/a&gt; too.&lt;/p&gt;
&lt;h3&gt;The fix&lt;/h3&gt;
&lt;p&gt;Now don't go panic over your own UrlRewrites just yet. This site had more than 2.500 rewritten URLs buried in urlrewrite.config. After being made aware, our contractor cut it down to 150 more generalized regular expressions, and voila, there's been a steady 3% CPU usage since then.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.aabech.no/media/1021/flatout.png" alt="CPU flattening out after cutting urlrewrites to 150" /&gt;&lt;/p&gt;
&lt;p&gt;I've been sceptical to people bad-mouthing UrlRewrite.Net in the past. We've never had many problems with it in our sites. But then again, we've never gone past 50 rewrite rules. (I think.) It's a great tool for its purpose, but as we've now witnessed, it can also do great harm when used wrong. It might be time for us to throw out the old IIS 6 servers and start using Microsoft's &lt;a href="https://www.iis.net/learn/extensions/url-rewrite-module/creating-rewrite-rules-for-the-url-rewrite-module"&gt;Url Rewrite Module&lt;/a&gt; instead.&lt;/p&gt;
&lt;h3&gt;Alternatives&lt;/h3&gt;
&lt;p&gt;But what should we do, then, when faced with situations where we have thousands of URLs to keep 301'ing? I'd guess you've got some kind of pattern there so you can simplify with catching regular expressions like we did here. Otherwise, you're probably better off &lt;a href="https://msdn.microsoft.com/en-us/library/ms227673.aspx"&gt;writing your own small HttpModule&lt;/a&gt; with some logic, rather than relying on generic rewriters and regex.&lt;/p&gt;
</description>
      <pubDate>Mon, 09 Jan 2017 22:03:53 Z</pubDate>
      <a10:updated>2017-01-09T22:03:53Z</a10:updated>
    </item>
  </channel>
</rss>