<?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">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">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">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">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">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>
  </channel>
</rss>