<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-AU" xmlns:media="http://search.yahoo.com/mrss/">
  <id>https://david.gardiner.net.au/tags/Unit%20Testing.xml</id>
  <title type="html">David Gardiner - Unit Testing</title>
  <updated>2026-05-15T00:32:33.409Z</updated>
  <subtitle>Blog posts tagged with &apos;Unit Testing&apos; - A blog of software development, .NET and other interesting things</subtitle>
  <rights>Copyright 2026 David Gardiner</rights>
  <icon>https://www.gravatar.com/avatar/37edf2567185071646d62ba28b868fab?s=64</icon>
  <logo>https://www.gravatar.com/avatar/37edf2567185071646d62ba28b868fab?s=256</logo>
  <generator uri="https://github.com/flcdrg/astrojs-atom" version="3">astrojs-atom</generator>
  <author>
    <name>David Gardiner</name>
  </author>
  <link href="https://david.gardiner.net.au/tags/Unit%20Testing.xml" rel="self" type="application/atom+xml"/>
  <link href="https://david.gardiner.net.au/tags/Unit%20Testing" rel="alternate" type="text/html" hreflang="en-AU"/>
  <category term="Unit Testing"/>
  <category term="Software Development"/>
  <entry>
    <id>https://david.gardiner.net.au/2025/07/verify-cli</id>
    <updated>2025-07-28T07:00:00.000+09:30</updated>
    <title>Verify.Cli - Snapshot testing without tests</title>
    <link href="https://david.gardiner.net.au/2025/07/verify-cli" rel="alternate" type="text/html" title="Verify.Cli - Snapshot testing without tests"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2025-07-28T07:00:00.000+09:30</published>
    <summary type="html">Verify.Cli is a new command-line tool for performing snapshot testing on individual files, without needing to create unit tests. Find out why I created this tool and examples of using it.</summary>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;/2025/07/verify&quot;&gt;my last post&lt;/a&gt;, I looked at using the &lt;a href=&quot;https://github.com/VerifyTests/Verify&quot;&gt;Verify&lt;/a&gt; library to implement snapshot testing in .NET unit tests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj_Zft2EW.webp&quot; alt=&quot;Verify logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve been doing quite a lot of &apos;DevOps&apos; work in recent years, and a large part of that has involved creating and optimising build and deployment (CI/CD) pipelines. Not infrequently I&apos;ve wanted to add a sanity check to a pipeline to ensure a file has the correct content. &quot;Oh if only I could call Verify from my pipeline&quot;, I&apos;d think.&lt;/p&gt;
&lt;p&gt;If the file was in version control, I could sometimes fallback to relying on &lt;code&gt;git diff&lt;/code&gt; to tell if the file had changed. But if it was a generated file that technique wouldn&apos;t work. Other file comparison tools (like regular &lt;code&gt;diff&lt;/code&gt;) might help, unless you had things like timestamps or other elements in the file that you wanted to ignore for the comparison. This is all bread and butter for Verify.&lt;/p&gt;
&lt;p&gt;And so I created &lt;a href=&quot;https://github.com/flcdrg/Verify.Cli&quot;&gt;Verify.Cli&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Verify.Cli&lt;/h2&gt;
&lt;p&gt;Verify.Cli is a command-line tool that uses Verify internally. But while Verify was originally written to be used in unit tests, it is possible to host it in a console application and get similar functionality. It can be used to perform snapshot testing on an individual file.&lt;/p&gt;
&lt;p&gt;You can install Verify.CLI as a .NET global tool, or there&apos;s a Docker image so you can run it from a container.&lt;/p&gt;
&lt;h2&gt;Case study - feed.xml&lt;/h2&gt;
&lt;p&gt;I&apos;ve recently rebuilt my blog using Astro. It generates a static website, but there is a challenge. Astro and the other libraries that are used to build my blog are updated frequently. It would be great to have confidence that those updates are not breaking the site. If it was a regular software application I could write some unit or integration tests. Given it generates static files, then the idea of snapshot testing some specific files would be helpful.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;feed.xml&lt;/code&gt; file is created when you run &lt;code&gt;pnpm build&lt;/code&gt; in the &lt;code&gt;dist&lt;/code&gt; folder. Here&apos;s part of it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot; xml:lang=&quot;en-AU&quot; xmlns:media=&quot;http://search.yahoo.com/mrss/&quot;&amp;gt;
  &amp;lt;id&amp;gt;https://david.gardiner.net.au/feed.xml&amp;lt;/id&amp;gt;
  &amp;lt;title type=&quot;html&quot;&amp;gt;David Gardiner&amp;lt;/title&amp;gt;
  &amp;lt;updated&amp;gt;2025-07-26T10:39:59.564Z&amp;lt;/updated&amp;gt;
  &amp;lt;subtitle&amp;gt;A blog of software development, .NET and other interesting things&amp;lt;/subtitle&amp;gt;
  &amp;lt;generator uri=&quot;https://github.com/flcdrg/astrojs-atom&quot; version=&quot;1.0.48&quot;&amp;gt;astrojs-atom&amp;lt;/generator&amp;gt;
  &amp;lt;author&amp;gt;
    &amp;lt;name&amp;gt;David Gardiner&amp;lt;/name&amp;gt;
  &amp;lt;/author&amp;gt;
  &amp;lt;link href=&quot;https://david.gardiner.net.au/feed.xml&quot; rel=&quot;self&quot; type=&quot;application/atom+xml&quot;/&amp;gt;
  &amp;lt;link href=&quot;https://david.gardiner.net.au/&quot; rel=&quot;alternate&quot; type=&quot;text/html&quot; hreflang=&quot;en-AU&quot;/&amp;gt;
  &amp;lt;entry&amp;gt;
    &amp;lt;id&amp;gt;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&amp;lt;/id&amp;gt;
    &amp;lt;updated&amp;gt;2025-07-14T08:00:00.000+09:30&amp;lt;/updated&amp;gt;
    &amp;lt;title&amp;gt;Azure Pipelines template expressions&amp;lt;/title&amp;gt;
    &amp;lt;link href=&quot;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&quot; rel=&quot;alternate&quot; type=&quot;text/html&quot; title=&quot;Azure Pipelines template expressions&quot;/&amp;gt;
    &amp;lt;category term=&quot;Azure Pipelines&quot;/&amp;gt;
    &amp;lt;published&amp;gt;2025-07-14T08:00:00.000+09:30&amp;lt;/published&amp;gt;
    &amp;lt;summary type=&quot;html&quot;&amp;gt;
      &amp;lt;![CDATA[Template expressions are a compile-time feature of Azure Pipelines. Learn how they differ to custom conditions
and see some common examples of their usage.]]&amp;gt;
    &amp;lt;/summary&amp;gt;
    &amp;lt;content type=&quot;html&quot; xml:base=&quot;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&quot;&amp;gt;
      &amp;lt;![CDATA[&amp;lt;p&amp;gt;In my &amp;lt;a href=&quot;/2025/06/azure-pipeline-conditionals&quot;&amp;gt;last post&amp;lt;/a&amp;gt; I wrote about using custom conditions in Azure Pipelines to evaluate whether to skip a step, job or stage at runtime.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Sometimes we can do better though. With template expressions we can not just skip something, we can remove it entirely. We can also use it to optionally insert values in a pipeline (something you can&apos;t do with runtime custom conditions).&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;The important thing to remember is that template expression are a &quot;compile time&quot; feature. They can only operate on things that are available at compile time. &amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/process/set-variables-scripts?view=azure-devops&amp;amp;amp;WT.mc_id=DOP-MVP-5001655&quot;&amp;gt;Variables set by scripts&amp;lt;/a&amp;gt;, and &amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/process/variables?view=azure-devops&amp;amp;amp;tabs=yaml%2Cbatch&amp;amp;amp;WT.mc_id=DOP-MVP-5001655#use-output-variables-from-tasks&quot;&amp;gt;task output variables&amp;lt;/a&amp;gt; are two examples of things that are not available at compile time.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Compare these two Azure Pipeline runs. The first uses custom conditions to decided if the &apos;Publish Artifact&apos; step is executed or not. Notice the &apos;Publish Artifact&apos; step is listed, but the icon shown is a white arrow (rather than green tick)
&amp;lt;img src=&quot;https://david.gardiner.net.au/_astro/azure-pipelines-custom-conditions.Be6IaBQz_101Uki.webp&quot; alt=&quot;Job showing a step &apos;Publish Artifact&apos; that was conditionally not executed&quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;If we use a template expression, then if it evaluates to false then the step is not even included in the job!&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;img src=&quot;https://david.gardiner.net.au/_astro/azure-pipelines-template-expressions.kRV13og-_2f5chf.webp&quot; alt=&quot;Job without a &apos;Publish Artifact&apos; step &quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/process/template-expressions?view=azure-devops&amp;amp;amp;WT.mc_id=DOP-MVP-5001655&quot;&amp;gt;Template expressions&amp;lt;/a&amp;gt; use the syntax &amp;lt;code&amp;gt;${{ }}&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;You can reference &amp;lt;code&amp;gt;parameters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;variables&amp;lt;/code&amp;gt; in template expressions. The latter are only variables that are defined in the YAML file and most of the &amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/build/variables?view=azure-devops&amp;amp;amp;WT.mc_id=DOP-MVP-5001655&quot;&amp;gt;predefined variables&amp;lt;/a&amp;gt;. (That page does list which variables can be used in template expressions, but you may need to scroll the page to the right to see that column!)&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;You can&apos;t reference variables that are created by scripts or anything else that is only available at runtime.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;You can use &amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/process/expressions?view=azure-devops&amp;amp;amp;WT.mc_id=DOP-MVP-5001655#functions&quot;&amp;gt;general functions&amp;lt;/a&amp;gt; (the same ones we used previously with runtime Custom Conditions) in template expressions, as well as two special &amp;lt;a href=&quot;https://learn.microsoft.com/azure/devops/pipelines/process/template-expressions?view=azure-devops&amp;amp;amp;WT.mc_id=DOP-MVP-5001655#template-expression-functions&quot;&amp;gt;Template expression functions&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;h2&amp;gt;Common patterns&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We run the Verify CLI against this file like this (after creating a &lt;code&gt;verified&lt;/code&gt; directory )&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;verify --file ./dist/feed.xml --verified-dir verified
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because there is no existing &lt;code&gt;verified/feed.xml.verified.xml&lt;/code&gt; file, the Verify library launches Beyond Compare (my diff tool of choice) and I can use it to create the verified file.&lt;/p&gt;
&lt;p&gt;However if I rebuild the site again and re-run verify, it shows up a difference. It turns out that the &lt;code&gt;&amp;lt;updated&amp;gt;2025-07-26T10:39:59.564Z&amp;lt;/updated&amp;gt;&lt;/code&gt; line is updated each time! Happily Verify&apos;s scrubbers can handle timestamps like that.&lt;/p&gt;
&lt;p&gt;If we were using Verify in a unit test, we&apos;d configure this scrubber using &lt;code&gt;VerifySettings.ScrubInlineDateTimes()&lt;/code&gt;. The equivalent for the command-line tool is &lt;code&gt;--scrub-inline-datetime&lt;/code&gt;. If we run verify again using this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;verify --file dist/feed.xml --verified-dir verified --scrub-inline-datetime &quot;yyyy-MM-ddTHH:mm:ss.fffZ&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;ll see that the scrubber has replaced the timestamp with a placeholder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;updated&amp;gt;DateTime_1&amp;lt;/updated&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place we can repeat the validation with subsequent builds and be sure that the file should be equivalent. If the comparison ever fails in the future then we can be pretty confident it&apos;s caused by a change in our logic or an update to one of the libraries.&lt;/p&gt;
&lt;p&gt;Here&apos;s how I&apos;m using Verify.Cli in a GitHub Action workflow:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: Setup .NET
  uses: actions/setup-dotnet@v5
  with:
    dotnet-version: 9.x

- name: Install Verify.Cli
  run: dotnet tool install --global Verify.Cli

- name: Verify
  run: |
    verify --file dist/feed.xml --verified-dir verified --scrub-inline-datetime &quot;yyyy-MM-ddTHH:mm:ss.fffZ&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Case study - blog post&lt;/h2&gt;
&lt;p&gt;The other thing I was keen to keep an eye on was individual blog posts. Here&apos;s part of one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&amp;gt;
    &amp;lt;meta name=&quot;generator&quot; content=&quot;Astro v5.12.0&quot;&amp;gt;
    &amp;lt;link rel=&quot;canonical&quot; href=&quot;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&quot;&amp;gt;
    &amp;lt;meta name=&quot;description&quot; content=&quot;Template expressions are a compile-time feature of Azure Pipelines. Learn how they differ to custom conditions
and see some common examples of their usage.&quot;&amp;gt;&amp;lt;meta name=&quot;og:description&quot; content=&quot;Template expressions are a compile-time feature of Azure Pipelines. Learn how they differ to custom conditions
and see some common examples of their usage.&quot;&amp;gt;&amp;lt;meta name=&quot;og:title&quot; content=&quot;Azure Pipelines template expressions&quot;&amp;gt;&amp;lt;meta name=&quot;og:type&quot; content=&quot;article&quot;&amp;gt;&amp;lt;meta name=&quot;og:url&quot; content=&quot;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&quot;&amp;gt;&amp;lt;meta name=&quot;og:image&quot; content=&quot;/_astro/azure-pipelines-logo.B45UakAg.png&quot;&amp;gt;&amp;lt;meta name=&quot;og:image:width&quot; content=&quot;80&quot;&amp;gt;&amp;lt;meta name=&quot;og:image:height&quot; content=&quot;80&quot;&amp;gt;&amp;lt;meta name=&quot;og:image:alt&quot; content=&quot;Azure Pipelines logo&quot;&amp;gt;&amp;lt;script type=&quot;application/ld+json&quot;&amp;gt;{&quot;@context&quot;:&quot;https://schema.org&quot;,&quot;@type&quot;:&quot;BlogPosting&quot;,&quot;mainEntityOfPage&quot;:{&quot;@type&quot;:&quot;WebPage&quot;,&quot;@id&quot;:&quot;https://david.gardiner.net.au/2025/07/azure-pipeline-template-expression&quot;},&quot;headline&quot;:&quot;Azure Pipelines template expressions&quot;,&quot;description&quot;:&quot;Template expressions are a compile-time feature of Azure Pipelines. Learn how they differ to custom conditions\nand see some common examples of their usage.&quot;,&quot;image&quot;:&quot;/_astro/azure-pipelines-logo.B45UakAg.png&quot;,&quot;author&quot;:{&quot;@type&quot;:&quot;Person&quot;,&quot;name&quot;:&quot;David Gardiner&quot;,&quot;url&quot;:&quot;https://david.gardiner.net.au/&quot;},&quot;datePublished&quot;:&quot;2025-07-14T08:00:00.000+09:30&quot;}&amp;lt;/script&amp;gt;
    &amp;lt;title&amp;gt;Azure Pipelines template expressions&amp;lt;/title&amp;gt;
    
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/_astro/_slug_.CT3ttAlr.css&quot;&amp;gt;&amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;div class=&quot;nav-container&quot; data-astro-cid-pux6a34n&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of that should be pretty stable. I tried a similar approach to what I used for &lt;code&gt;feed.xml&lt;/code&gt;, and created a pull request then ran it in the GitHub Actions workflow... and it failed!&lt;/p&gt;
&lt;p&gt;When I looked closer I realised that I&apos;ve configured pull request build to use &lt;code&gt;pnpm build --devOutput&lt;/code&gt;, whilst main branch builds just use &lt;code&gt;pnpm build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One difference with the former is that it adds an extra attribute to &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements - &lt;code&gt;data-image-component=&quot;true&quot;&lt;/code&gt;. To be able to compare files we&apos;ll need to remove all instances of that.&lt;/p&gt;
&lt;p&gt;Verify.Cli has the &lt;code&gt;--scrub-inline-remove&lt;/code&gt; parameter that we can use for that. eg.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--scrub-inline-remove &apos; data-image-component=&quot;true&quot;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When Astro processes images, it does appear to generate pretty stable URLs, but just in case it might be best to exclude those as well. Here&apos;s a typical &lt;code&gt;img&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;/_astro/MVP_Logo_Horizontal_Preferred_Cyan300_RGB_300ppi.a-4xiaYx_2bYEEr.png&quot; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are not fixed strings - they are unique for each image, so using a regular expression would make sense. This time we&apos;ll use &lt;code&gt;--scrub-inline-pattern&lt;/code&gt; with an appropriate expression:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--scrub-inline-pattern &apos;(?&amp;lt;prefix&amp;gt;&quot;)/_astro/[^&quot;]+(?&amp;lt;suffix&amp;gt;&quot;)&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This parameter has an extra feature that I&apos;m taking advantage of. If you include named groups in the regular expression &apos;prefix&apos; and or &apos;suffix&apos;, then the scrubber will add those back to the start and end. I&apos;m using that so that the double quotes are maintained.&lt;/p&gt;
&lt;p&gt;The replacement looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;STRING_5&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s the full command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;verify --file dist/2025/07/azure-pipeline-template-expression.html --verified-dir verified --scrub-inline-pattern &apos;(?&amp;lt;prefix&amp;gt;&quot;)/_astro/[^&quot;]+(?&amp;lt;suffix&amp;gt;&quot;)&apos; --scrub-inline-remove &apos; data-image-component=&quot;true&quot;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the GitHub Action workflow is using the Docker image to run Verify.Cli (another way of using it instead of installing it as a .NET global tool):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: Verify post
  run: docker run --rm --user &quot;$(id -u):$(id -g)&quot; -v $PWD:/tmp flcdrg/verify-cli --file /tmp/dist/2025/07/azure-pipeline-template-expression.html --verified-dir /tmp/verified --scrub-inline-pattern &apos;(?&amp;lt;prefix&amp;gt;&quot;)/_astro/[^&quot;]+(?&amp;lt;suffix&amp;gt;&quot;)&apos; --scrub-inline-pattern &apos;(?&amp;lt;prefix&amp;gt;title=&quot;)[^&quot;]+(?&amp;lt;suffix&amp;gt;&quot;)&apos; --scrub-inline-remove &apos; data-image-component=&quot;true&quot;&apos; --scrub-inline-pattern &apos;(?&amp;lt;prefix&amp;gt;meta name=&quot;generator&quot; content=&quot;Astro v)[\d\.]+(?&amp;lt;suffix&amp;gt;&quot;)&apos; --verbosity detailed
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What if it fails?&lt;/h2&gt;
&lt;p&gt;If Verify.Cli fails locally on your machine, you can quickly identify the conflict via your visual diff tool of choice. But if it fails in a pipeline, then currently all you get is an error that the files are different. For now, I&apos;ve come up with a hacky workaround:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- name: Diff files if error
  if: failure()
  run: |
    diff -u dist/feed.xml verified/feed.xml.verified.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This step in the workflow only runs if the Verify.Cli step failed. It assumes you have the &lt;code&gt;diff&lt;/code&gt; tool installed on your build agent. This could be an aspect to improve in Verify.Cli in the future.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Take a look at &lt;a href=&quot;https://github.com/flcdrg/astro-blog-engine&quot;&gt;https://github.com/flcdrg/astro-blog-engine&lt;/a&gt; for a repo making use of Verify.Cli (including the &lt;a href=&quot;https://github.com/flcdrg/astro-blog-engine/blob/main/.github/workflows/main.yml&quot;&gt;main workflow&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If you have a need to use the snapshot testing technique on individual files outside of unit tests, please give Verify.Cli a go. Let me know in the comments what you think, and feel free to submit any suggestions or bug reports &lt;a href=&quot;https://github.com/flcdrg/Verify.Cli&quot;&gt;over at the repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Helmet by Leonidas Oikonomou from &lt;a href=&quot;https://thenounproject.com/browse/icons/term/helmet/&quot;&gt;Noun Project&lt;/a&gt; (CC BY 3.0)&lt;/p&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj.png" width="400" height="400"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj.png" width="400" height="400"/>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2025/07/verify</id>
    <updated>2025-07-21T20:00:00.000+09:30</updated>
    <title>Snapshot testing .NET code with Verify</title>
    <link href="https://david.gardiner.net.au/2025/07/verify" rel="alternate" type="text/html" title="Snapshot testing .NET code with Verify"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2025-07-21T20:00:00.000+09:30</published>
    <summary type="html">An introduction to using Verify for .NET snapshot testing</summary>
    <content type="html">&lt;p&gt;Snapshot testing has become a really popular way of asserting data has the correct shape and values in software unit tests. My first introduction to these kinds of tests was with &lt;a href=&quot;https://approvaltests.com/&quot;&gt;ApprovalTests&lt;/a&gt; for .NET. You may also have encountered them in JavaScript with &lt;a href=&quot;https://jestjs.io/docs/snapshot-testing&quot;&gt;Jest&lt;/a&gt;. But in my opinion the standout library for snapshot testing is Simon Cropp&apos;s excellent &lt;a href=&quot;https://github.com/VerifyTests/Verify&quot;&gt;Verify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj_Zft2EW.webp&quot; alt=&quot;Verify logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve written previously about Verify, specifically an extension I created for Verify to facilitate &lt;a href=&quot;/2022/03/verify-mongodb&quot;&gt;snapshot testing with MongoDB&lt;/a&gt;. One thing that sets Verify apart is the ability to customise and configure how it works for each test, particularly in the way it serialises and sanitises/scrubs the data.&lt;/p&gt;
&lt;p&gt;Data scrubbing (see &lt;a href=&quot;https://github.com/VerifyTests/Verify/blob/main/docs/scrubbers.md&quot;&gt;Scrubbers&lt;/a&gt;) is how you deal with values that may change between unit test runs, but within a run they must be consistent. You can choose to either remove values entirely, or replace them with special placeholders.&lt;/p&gt;
&lt;p&gt;For example, consider the following C# code that creates a dynamic JSON object:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var json = new
{
    id = Guid.NewGuid(),
    time = DateTimeOffset.Now,
    name = &quot;David Gardiner&quot;,
    currentUser = Environment.UserName
};

var text = System.Text.Json.JsonSerializer.Serialize(json);

var settings = new VerifySettings();
settings.ScrubUserName();

// Ensure received and verified files have .json suffix (otherwise it will be .txt)
settings.UseStrictJson();

return VerifyJson(text, settings);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual value of the JSON object will change every time the code is run, as it includes
values like a random GUID, the current date and time, and the current username. If you just compared a file that you&apos;d serialised this object to, then it wouldn&apos;t match if you compared it again a few seconds later.&lt;/p&gt;
&lt;p&gt;Instead, by the use of scrubbers and sanitisers, you end up with a file like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;id&quot;: &quot;Guid_1&quot;,
  &quot;time&quot;: &quot;DateTimeOffset_1&quot;,
  &quot;name&quot;: &quot;David Gardiner&quot;,
  &quot;currentUser&quot;: &quot;TheUserName&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that the GUID, time and current user values have all been replaced with placeholders names. The clever thing is that if you had other properties with the same original values, then Verify is smart enough to swap those all over too (so you could easily confirm that &lt;code&gt;Guid_1&lt;/code&gt; was used in all the right places).&lt;/p&gt;
&lt;p&gt;This means that your test will continue to pass if you run it 5 seconds later, or if you run it on a build server on the other side of the world with a different process username.&lt;/p&gt;
&lt;p&gt;The underlying values will have changed, but the shape and usage of them hasn&apos;t, and that&apos;s what you care about.&lt;/p&gt;
&lt;p&gt;There&apos;s a default set of scrubbers and sanitisers that you get out of the box, but you can enable additional ones or write your own. In the example above I opted in to username scrubbing by calling &lt;code&gt;ScrubUserName()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If necessary, you can adjust how &lt;a href=&quot;https://github.com/VerifyTests/Verify/blob/main/docs/dates.md&quot;&gt;Dates&lt;/a&gt; and &lt;a href=&quot;https://github.com/VerifyTests/Verify/blob/main/docs/guids.md&quot;&gt;GUIDs&lt;/a&gt; are handled. There&apos;s even more options in &lt;a href=&quot;https://github.com/VerifyTests/Verify/blob/main/docs/serializer-settings.md&quot;&gt;Serializer Settings&lt;/a&gt; and &lt;a href=&quot;https://github.com/VerifyTests/Verify/blob/main/docs/scrubbers.md&quot;&gt;Scrubbers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Snapshot tests dramatically simplify your unit tests - the alternative would be lots of specific property asserts, which can get pretty messy.&lt;/p&gt;
&lt;p&gt;The other aspect of Verify that really shines is how it can integrate with &lt;a href=&quot;https://github.com/VerifyTests/DiffEngine#supported-tools&quot;&gt;any of a number of popular GUI diff tools&lt;/a&gt; when you&apos;re running it interactively and a comparison fails.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/beyondcompare.POaTT2nm_Z2b50cM.webp&quot; alt=&quot;Screenshot of Beyond Compare showing the &apos;name&apos; line different because the received data includes a middle initial that isn&apos;t present in the verified data&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In that scenario, it will automatically launch your diff tool (&lt;a href=&quot;https://www.scootersoftware.com/&quot;&gt;Beyond Compare&lt;/a&gt; is my diff tool of choice) showing you the actual and expected (verified) text and you can easily identify if the failure represents a bug that needs to be fixed, or alternatively it might be an expected change so you can easily update the verified content.&lt;/p&gt;
&lt;p&gt;It&apos;s worth noting that Verify is smart enough to realise when it is running non-interactively (like on a build server), and in that case it just reports the comparison failed, causing the related unit test to fail. No GUI diff tools are launched.&lt;/p&gt;
&lt;p&gt;In my next post I&apos;ll show how I took Verify and found a way to use it outside of unit tests.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side note: The code samples in this blog post were included by using one of Simon&apos;s other projects - &lt;a href=&quot;https://github.com/SimonCropp/MarkdownSnippets&quot;&gt;https://github.com/SimonCropp/MarkdownSnippets&lt;/a&gt;. A really clever tool for embedding code samples in Markdown files, which has the advantage that the code samples are more likely to be valid code as you can compile the original source files&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Helmet by Leonidas Oikonomou from &lt;a href=&quot;https://thenounproject.com/browse/icons/term/helmet/&quot;&gt;Noun Project&lt;/a&gt; (CC BY 3.0)&lt;/p&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj.png" width="400" height="400"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/verify-tests.CHaOWxZj.png" width="400" height="400"/>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2022/03/verify-mongodb</id>
    <updated>2022-03-31T08:00:00.000+10:30</updated>
    <title>Snapshot testing Verify.MongoDB</title>
    <link href="https://david.gardiner.net.au/2022/03/verify-mongodb" rel="alternate" type="text/html" title="Snapshot testing Verify.MongoDB"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2022-03-31T08:00:00.000+10:30</published>
    <summary type="html">Verify is a snapshot tool created by Simon Cropp.</summary>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://github.com/VerifyTests/Verify&quot;&gt;&lt;code&gt;Verify&lt;/code&gt;&lt;/a&gt; is a snapshot tool created by &lt;a href=&quot;https://github.com/SimonCropp&quot;&gt;Simon Cropp&lt;/a&gt;. It takes inspiration from &lt;a href=&quot;https://github.com/approvals/ApprovalTests.Net&quot;&gt;&lt;code&gt;ApprovalTests&lt;/code&gt;&lt;/a&gt; and makes it easy to assert complex data models and documents (e.g. as part of a unit test).&lt;/p&gt;
&lt;p&gt;What I like about this technique is that if the data model or document is different to what the test expects, then not only does the test fail, but for local development, it can automatically launch familiar diff tools. One of my favourites is Beyond Compare, and it makes it very easy to identify what the actual differences are.&lt;/p&gt;
&lt;p&gt;In addition to the main Verify library, Simon and others have also created extension packages to provide additional support for more specific cases (like &lt;a href=&quot;https://github.com/VerifyTests/Verify.AspNetCore&quot;&gt;&lt;code&gt;Verify.AspNetCore&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/VerifyTests/Verify.EntityFramework&quot;&gt;&lt;code&gt;Verify.EntityFramework&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/VerifyTests/Verify.ImageSharp&quot;&gt;&lt;code&gt;Verify.ImageSharp&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/VerifyTests/Verify.NServiceBus&quot;&gt;&lt;code&gt;Verify.NServiceBus&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/VerifyTests/Verify#user-content-extensions&quot;&gt;more&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I was doing some work with &lt;a href=&quot;https://learn.microsoft.com/azure/cosmos-db/overview?WT.mc_id=DOP-MVP-5001655&quot;&gt;Azure Cosmos DB&lt;/a&gt; using the &lt;a href=&quot;https://learn.microsoft.com/azure/cosmos-db/mongodb/overview?WT.mc_id=DOP-MVP-5001655&quot;&gt;Mongo API&lt;/a&gt; with .NET and thought it would be useful to be able to write some tests to capture what actual queries are being sent over the wire.&lt;/p&gt;
&lt;p&gt;I&apos;d recently listened to an episode of &lt;a href=&quot;https://unhandledexceptionpodcast.com/&quot;&gt;The Unhandled Exception podcast&lt;/a&gt; where &lt;a href=&quot;https://unhandledexceptionpodcast.com/posts/0029-snapshottesting/&quot;&gt;Dan Clarke interviewed Simon on Snapshot Testing&lt;/a&gt;. He gave the example of using the &lt;code&gt;Verify.EntityFramework&lt;/code&gt; extension package to write unit tests that would validate the SQL that Entity Framework was generating.&lt;/p&gt;
&lt;p&gt;This made me wonder if I could do something similar for MongoDB. After reviewing how the &lt;code&gt;Verify.EntityFramework&lt;/code&gt; extension worked, I took a closer look at the &lt;a href=&quot;https://mongodb.github.io/mongo-csharp-driver/&quot;&gt;MongoDB .NET Driver&lt;/a&gt; library to see what hooks were available. After a bit of trial and error, I figured out how it was possible!&lt;/p&gt;
&lt;p&gt;You can write a unit test that includes code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MongoDBRecording.StartRecording();

await collection.FindAsync(Builders&amp;lt;BsonDocument&amp;gt;.Filter.Eq(&quot;_id&quot;, &quot;blah&quot;),
    new FindOptions&amp;lt;BsonDocument, BsonDocument&amp;gt;());
    
await Verifier.Verify(&quot;collection&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The verified file would have the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  target: collection,
  mongo: [
    {
      Database: VerifyTests,
      Document: {
        filter: {
          _id: blah
        },
        find: docs
      },
      Type: Started,
      Command: find,
      StartTime: DateTimeOffset_1,
      OperationId: Id_1,
      RequestId: Id_2
    },
    {
      Document: {
        cursor: {
          firstBatch: [],
          id: 0,
          ns: VerifyTests.docs
        },
        ok: 1.0
      },
      Type: Succeeded,
      Command: find,
      StartTime: DateTimeOffset_2,
      OperationId: Id_1,
      RequestId: Id_2
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s a representation of what would be sent over the wire by the query in the test. It&apos;s an ideal opportunity to confirm that the query is doing what you intended. For pay-per-use services like Cosmos DB, it&apos;s critical that your queries are as efficient as possible. Otherwise, it might cost you too much, and your queries &lt;a href=&quot;https://learn.microsoft.com/azure/cosmos-db/optimize-cost-throughput?WT.mc_id=DOP-MVP-5001655&quot;&gt;might end up rate-limited&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After confirming it worked as I&apos;d hoped, I figured it could be something that others might find useful, so I created a NuGet package. I got in touch with Simon to find out how best to get it published on nuget.org. He was most helpful, and I&apos;m pleased to report that the package is now available at &lt;a href=&quot;https://www.nuget.org/packages/Verify.MongoDB/&quot;&gt;https://www.nuget.org/packages/Verify.MongoDB/&lt;/a&gt;, and the source repository is at &lt;a href=&quot;https://github.com/flcdrg/Verify.MongoDB&quot;&gt;https://github.com/flcdrg/Verify.MongoDB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&apos;re building an application that&apos;s using the MongoDB .NET Driver then this package will help you create some useful snapshot tests.&lt;/p&gt;
&lt;p&gt;Check it out!&lt;/p&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/mongodb.DxXR8Ye5.png" width="990" height="257"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/mongodb.DxXR8Ye5.png" width="990" height="257"/>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2020/11/debugging-jest-vscode</id>
    <updated>2020-11-10T12:00:00.000+10:30</updated>
    <title>The case of the disappearing &apos;Debug&apos; CodeLens in Visual Studio Code</title>
    <link href="https://david.gardiner.net.au/2020/11/debugging-jest-vscode" rel="alternate" type="text/html" title="The case of the disappearing &apos;Debug&apos; CodeLens in Visual Studio Code"/>
    <category term="Unit Testing"/>
    <category term="Visual Studio Code"/>
    <published>2020-11-10T12:00:00.000+10:30</published>
    <summary type="html">*Or how to debug any Jest unit test in Visual Studio Code*</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Or how to debug any Jest unit test in Visual Studio Code&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m trying to debug a TypeScript unit test which uses the Jest library. There&apos;s a nice VS Code extension &lt;a href=&quot;https://github.com/jest-community/vscode-jest&quot;&gt;vs-jest&lt;/a&gt; that integrates with Jest and even adds CodeLens labels so you can click to debug a specific test. Except the debug label kept disappearing! It would show when I first loaded the folder in Code, but after the tests all ran then the label would go away. Even though the test is passing, I wanted to debug it so I could step through the code. What&apos;s going on?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/vscode-jest-debug-missing.IbIyQjlD_1QhfIU.webp&quot; alt=&quot;Screenshot showing &apos;Debug&apos; going away&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There&apos;s some troubleshooting tips listed on the &lt;a href=&quot;https://github.com/jest-community/vscode-jest/blob/master/README.md#troubleshooting&quot;&gt;vscode-jest README&lt;/a&gt;. This gave me the hint that there are some config settings that can alter how the extension behaves. When I viewed my current settings.json, I saw this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;typescript.tsdk&quot;: &quot;node_modules\\typescript\\lib&quot;,
    &quot;eslint.packageManager&quot;: &quot;yarn&quot;,
    &quot;eslint.validate&quot;: [
        &quot;javascript&quot;,
        {&quot;language&quot;: &quot;typescript&quot;, &quot;autoFix&quot;: true }
    ],
    &quot;jest.debugCodeLens.showWhenTestStateIn&quot;: [
        &quot;fail&quot;,
        &quot;unknown&quot;
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That last setting caught my attention. Clicking inside that array and code completion offered two other values &quot;pass&quot; and &quot;skip&quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &quot;jest.debugCodeLens.showWhenTestStateIn&quot;: [
       &quot;pass&quot;,
       &quot;skip&quot;,
        &quot;fail&quot;,
        &quot;unknown&quot;
    ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding those and hitting save, and then the &apos;debug&apos; labels returned and stayed for all the tests!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2017/12/unit-test-fixtures</id>
    <updated>2017-12-01T21:27:00.000+10:30</updated>
    <title>Unit Test Fixtures</title>
    <link href="https://david.gardiner.net.au/2017/12/unit-test-fixtures" rel="alternate" type="text/html" title="Unit Test Fixtures"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2017-12-01T21:27:00.000+10:30</published>
    <summary type="html">Almost ten years ago, I bought and read Gerard Meszaros’ xUnit Test Patterns, Refactoring Test Code.</summary>
    <content type="html">&lt;p&gt;Almost ten years ago, I bought and read Gerard Meszaros’ &lt;a href=&quot;https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054/ref=as_li_ss_tl?ie=UTF8&amp;amp;linkCode=sl1&amp;amp;tag=davesdayd-20&amp;amp;linkId=d392b7c9f126d8bbacb012d2d67eda5c&quot;&gt;xUnit Test Patterns, Refactoring Test Code&lt;/a&gt;. It is very comprehensive and seeks to document all the common test design patterns. Meszaros uses italics to indicate named patterns, and I&apos;ll follow that convention here.&lt;/p&gt;
&lt;p&gt;The unit test framework I’ve used the most with .NET is &lt;a href=&quot;https://nunit.org/&quot;&gt;NUnit&lt;/a&gt;. One quirk that I’m just beginning to understand better is the difference between &lt;em&gt;Testcase Class&lt;/em&gt; and &lt;em&gt;Fixture&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;My confusion lies with how NUnit requires that classes which contain test methods, be decorated with the &lt;code&gt;TestFixture&lt;/code&gt; attribute. I always assumed that this meant the class was &quot;the fixture&quot;, but no!&lt;/p&gt;
&lt;p&gt;Meszaros defines them as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(I) use ‘test fixture’ or just ‘fixture’ to mean ‘the pre-conditions of the test’ and Testcase class to mean ‘the class that contains the Test Methods and any code needed to set up the test fixture’. (xUnit Test Patterns p.59)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The key thing is that while NUnit defaults to the &lt;em&gt;Testcase Class per Fixture&lt;/em&gt;, you can do things differently.&lt;/p&gt;
&lt;p&gt;Take a look at one of the unit test projects from the &lt;a href=&quot;https://github.com/cake-build/cake&quot;&gt;Cake project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2017/12/cake.nuget.tests%20project.png&quot; alt=&quot;Cake.NuGet.Tests project structure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Notice how they’ve separated the &lt;em&gt;Fixture&lt;/em&gt; classes and the &lt;em&gt;Testcase classes&lt;/em&gt; into separate folders.&lt;/p&gt;
&lt;p&gt;Here’s one of the test methods from the &lt;code&gt;NuGetPackageInstallerTests&lt;/code&gt; class. Note how concise and uncomplicated it is. You can look at this method and very quickly understand what it’s testing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
[Fact]
public void Should_Be_Able_To_Install_If_Scheme_Is_Correct()
{
    // Given
    var fixture = new NuGetPackageInstallerFixture();
    fixture.Package = new PackageReference(&quot;nuget:?package=Cake.Core&quot;);

    // When
    var result = fixture.CanInstall();

    // Then
    Assert.True(result);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s look at the fixture class – &lt;code&gt;NuGetPackageInstallerFixture&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;internal sealed class NuGetPackageInstallerFixture
{
    public ICakeEnvironment Environment { get; set; }
    public FakeFileSystem FileSystem { get; set; }
    public IProcessRunner ProcessRunner { get; set; }
    public INuGetToolResolver ToolResolver { get; set; }
    public INuGetContentResolver ContentResolver { get; set; }
    public ICakeLog Log { get; set; }

    public PackageReference Package { get; set; }
    public PackageType PackageType { get; set; }
    public DirectoryPath InstallPath { get; set; }

    public ICakeConfiguration Config { get; set; }

    public NuGetPackageInstallerFixture()
    {
        Environment = FakeEnvironment.CreateUnixEnvironment();
        FileSystem = new FakeFileSystem(Environment);
        ContentResolver = Substitute.For&amp;lt;INuGetContentResolver&amp;gt;();
        Log = Substitute.For&amp;lt;ICakeLog&amp;gt;();
        Config = Substitute.For&amp;lt;ICakeConfiguration&amp;gt;();

        ToolResolver = Substitute.For&amp;lt;INuGetToolResolver&amp;gt;();
        ToolResolver.ResolvePath().Returns(new FilePath(&quot;/Working/tools/nuget.exe&quot;));

        Package = new PackageReference(&quot;nuget:https://myget.org/temp/?package=Cake.Foo&amp;amp;prerelease&amp;amp;version=1.2.3&quot;);
        PackageType = PackageType.Addin;
        InstallPath = new DirectoryPath(&quot;./nuget&quot;);

        ProcessRunner = Substitute.For&amp;lt;IProcessRunner&amp;gt;();
        ProcessRunner.When(p =&amp;gt; p.Start(Arg.Any&amp;lt;FilePath&amp;gt;(), Arg.Any&amp;lt;ProcessSettings&amp;gt;()))
            .Do(info =&amp;gt; FileSystem.CreateDirectory(InstallPath.Combine(Package.Package.ToLowerInvariant()).Combine(Package.Package)));
    }

    public void InstallPackageAtSpecifiedPath(DirectoryPath path)
    {
        ProcessRunner.When(p =&amp;gt; p.Start(Arg.Any&amp;lt;FilePath&amp;gt;(), Arg.Any&amp;lt;ProcessSettings&amp;gt;()))
            .Do(info =&amp;gt; FileSystem.CreateDirectory(path));
    }

    public NuGetPackageInstaller CreateInstaller()
    {
        return new NuGetPackageInstaller(FileSystem, Environment, ProcessRunner, ToolResolver, ContentResolver, Log, Config);
    }

    public IReadOnlyCollection&amp;lt;IFile&amp;gt; Install()
    {
        var installer = CreateInstaller();
        return installer.Install(Package, PackageType, InstallPath);
    }

    public bool CanInstall()
    {
        var installer = CreateInstaller();
        return installer.CanInstall(Package, PackageType);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As described in xUnit Test Patterns, this &lt;em&gt;Test Fixture&lt;/em&gt; has everything we need in place to exercise the &lt;em&gt;SUT&lt;/em&gt; (system under test). It instantiates both the &lt;em&gt;SUT&lt;/em&gt; and all its dependencies, and also provides helper methods that are called by the &lt;em&gt;Test Methods&lt;/em&gt;. Without this, all of the setup and configuration code would have lived in the &lt;em&gt;Testcase class&lt;/em&gt; (&lt;code&gt;NuGetPackageInstallerTests&lt;/code&gt;). This looks like an example of the &lt;a href=&quot;http://xunitpatterns.com/Fresh%20Fixture%20Management.html&quot;&gt;Transient Fresh Fixture&lt;/a&gt; pattern.&lt;/p&gt;
&lt;p&gt;If you want to get another perspective on using test fixtures, I’d also recommend taking a look at Mark Seemann’s &lt;a href=&quot;https://www.pluralsight.com/courses/advanced-unit-testing&quot;&gt;Advanced Unit Testing course on Pluralsight&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2016/08/ddd-melbourne-2016</id>
    <updated>2016-08-20T19:58:00.001+09:30</updated>
    <title>DDD Melbourne 2016</title>
    <link href="https://david.gardiner.net.au/2016/08/ddd-melbourne-2016" rel="alternate" type="text/html" title="DDD Melbourne 2016"/>
    <category term="Conferences"/>
    <category term="Talks"/>
    <category term="Unit Testing"/>
    <published>2016-08-20T19:58:00.001+09:30</published>
    <summary type="html">Around 350 people were there.</summary>
    <content type="html">
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_003%5b3%5d.jpg&quot; alt=&quot;Sign for Monash University&quot; /&gt;Last weekend I was privileged to be able to attend my second &lt;a href=&quot;http://dddmelbourne.com/&quot;&gt;DDD Melbourne&lt;/a&gt; conference. I flew over Friday afternoon and had an uneventful transit from Melbourne airport to the motel, via the Skybus and a train connection. I stayed locally near Monash University’s Caufield campus, the venue for this year’s event.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_005%5b4%5d.jpg&quot; alt=&quot;Welcome and intro to DDD Melbourne&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Around 350 people were there. Monash was a great venue, with all lecture theatres having excellent AV facilities, so no problems watching presentations or hearing speakers. It was also really nice to catch up with a few familiar faces.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_008%5b4%5d.jpg&quot; alt=&quot;Paul Fenwick with slide of 1970&apos;s sci fi robots&quot; /&gt;The day started with a thought-provoking keynote from Paul Fenwick, making us stop and think about what kind of future we’re creating. That slide with the strange robots – I had that book as a child! 😀
&lt;img src=&quot;../../assets/2016/08/wp_20160813_010%5b4%5d.jpg&quot; alt=&quot;Morning tea break&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After that I caught the following sessions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Thinking in React&lt;/em&gt; with Mohammad Helmy&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Deploying Straight to Production: A Guide to the Holy Grail&lt;/em&gt; with Damian Brady&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The Actor/Model pattern in .NET: Akka.Net vs Orleans for the curious&lt;/em&gt; with William Tulloch&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Better APIs with GraphQL&lt;/em&gt; with Josh Price&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_017%5b4%5d.jpg&quot; alt=&quot;Lunch break&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After lunch I also got to present &lt;em&gt;10 tools and libraries to enhance .NET Unit Testing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_023%5b4%5d.jpg&quot; alt=&quot;Audience before closing presentation&quot; /&gt;The final talk for the day was from Shawn Wildermuth. An inspiring and personal look into the life of a developer, and a great way to end the day.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/2016/08/wp_20160813_028%5b4%5d.jpg&quot; alt=&quot;Shawn talking about active listening&quot; /&gt;One key point Shawn made was how critical it is if you want to be a good developer, is to really work in your listening skills – not just “hearing” but actual ‘active listening’, because as it turns out, good communication skills are more important that good technical skills.&lt;/p&gt;
&lt;p&gt;And with that DDD Melbourne was over for another year. Off to the airport for the flight back home late Saturday night to be back at home with family and my own bed.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2014/11/testing-for-exceptions-with-nunit</id>
    <updated>2014-11-20T21:21:00.001+10:30</updated>
    <title>Testing for Exceptions with NUnit</title>
    <link href="https://david.gardiner.net.au/2014/11/testing-for-exceptions-with-nunit" rel="alternate" type="text/html" title="Testing for Exceptions with NUnit"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2014-11-20T21:21:00.001+10:30</published>
    <summary type="html">I’ve been aware for a long time usual way of writing a unit test with NUnit that expects an Exception to be thrown is to use the ExpectedExceptionAttribute on the test method.</summary>
    <content type="html">&lt;p&gt;I’ve been aware for a long time usual way of writing a unit test with &lt;a href=&quot;https://nunit.org/&quot;&gt;NUnit&lt;/a&gt; that expects an Exception to be thrown is to use the &lt;a href=&quot;https://nunit.org/index.php/?p=exception&amp;amp;r=2.6.3&quot;&gt;ExpectedExceptionAttribute&lt;/a&gt; on the test method.&lt;/p&gt;
&lt;p&gt;[ExpectedException(typeof(ArgumentException)]
[Test]
public void Test()
{
    var sut = new ThrowUp();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sut.DodgyMethod(3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;I hadn’t noticed that around the release of NUnit 2.5, an alternative was added. The &lt;a href=&quot;https://nunit.org/index.php/?p=assertThrows&amp;amp;r=2.5&quot;&gt;Assert.Throws&lt;/a&gt; method allows you to be specific about which bit of code in the test should be throwing an exception.&lt;/p&gt;
&lt;p&gt;public void Test()
{
    var sut = new ThrowUp();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Assert.Throws&amp;lt;ArgumentException&amp;gt;( () =&amp;gt; sut.DodgyMethod(3) );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;I think it’s a good improvement, and makes it a bit clearer where the exception should be coming from.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2011/01/unit-testing-your-silverlightwp7-code</id>
    <updated>2011-01-20T10:54:00.001+10:30</updated>
    <title>Unit testing your Silverlight/WP7 code with Portable Library Tools?</title>
    <link href="https://david.gardiner.net.au/2011/01/unit-testing-your-silverlightwp7-code" rel="alternate" type="text/html" title="Unit testing your Silverlight/WP7 code with Portable Library Tools?"/>
    <category term=".NET"/>
    <category term="Windows Phone"/>
    <category term="Unit Testing"/>
    <published>2011-01-20T10:54:00.001+10:30</published>
    <summary type="html">I&apos;m slowly improving my knowledge of developing in Silverlight and specifically apps for Windows Phone 7.</summary>
    <content type="html">&lt;p&gt;I&apos;m slowly improving my knowledge of developing in Silverlight and specifically apps for Windows Phone 7. My &lt;a href=&quot;/2010/12/internode-usage-meter-for-windows-phone&quot;&gt;Internode Usage app&lt;/a&gt; has been out for almost a month, and I&apos;m working on an update for that (including real Internode logos now that I&apos;ve got permission). I&apos;ve also been creating a new app that will display the nearest public toilet (particularly useful for parents of young kids, especially if you&apos;re travelling). That&apos;s been fun as I&apos;ve got to use the &lt;a href=&quot;https://learn.microsoft.com/previous-versions/windows/&quot;&gt;Map control&lt;/a&gt;. I hope to publish that soon.&lt;/p&gt;
&lt;p&gt;One thing I have missed was the ability to easily write unit tests for the code. The problem is that the code doesn&apos;t run on your desktop, it runs in the emulator or on a device. This has meant that &lt;a href=&quot;http://codingsolutions.blogspot.com/2010/03/steps-to-run-windows-phone-7-unit-test.html&quot;&gt;most&lt;/a&gt; &lt;a href=&quot;https://www.smartypantscoding.com/a-cheat-sheet-for-unit-testing-silverlight-apps-on-windows-phone-7&quot;&gt;of the&lt;/a&gt; &lt;a href=&quot;https://web.archive.org/web/20220113201020/https://www.jeff.wilcox.name/2010/05/sl3-utf-bits/&quot;&gt;solutions&lt;/a&gt; so far have relied on running the test runner on the device as a separate application. That is certainly a valid way to test, but getting the results of the test back out isn&apos;t easy. Justin Angel &lt;a href=&quot;http://justinangel.net/WindowsPhone7EmulatorAutomation&quot;&gt;describes one approach&lt;/a&gt; using the CoreCon API to read and write to the test app&apos;s Isolated Storage. Another way might be to get the test runner app to push the results back out to some kind of HTTP listener running externally to the emulator.&lt;/p&gt;
&lt;p&gt;Enter the &lt;a href=&quot;https://web.archive.org/web/20150810190139/http://blogs.msdn.com/b/bclteam/archive/2011/01/19/announcing-portable-library-tools-ctp-justin-van-patten.aspx&quot;&gt;Portable Library Tools&lt;/a&gt; add-in. Just &lt;a href=&quot;https://marketplace.visualstudio.com/&quot;&gt;released as a CTP&lt;/a&gt;, this allows code to be targeted at both Windows Phone 7 and the regular CLR (as well as Silverlight and XNA) without having to jump through lots of hoops.&lt;/p&gt;
&lt;p&gt;It &lt;em&gt;should&lt;/em&gt; now be possible to create a regular .NET unit testing assembly (say using &lt;a href=&quot;http://www.gallio.org&quot;&gt;MbUnit with the Gallio test runner&lt;/a&gt;) and run tests over most of your code, and then just recompile to have the same code be deployed to WP7.&lt;/p&gt;
&lt;p&gt;There will be exceptions, as you may need to refactor your code to extract out specific WP7 dependencies (that obviously have no equivalent in the desktop .NET Framework), but this looks like a promising step forward.&lt;/p&gt;
&lt;p&gt;One caveat is that to install the Portable Library Tools, you need to have installed &lt;a href=&quot;http://go.microsoft.com/fwlink/?LinkId=207130&quot;&gt;VS 2010 SP1 Beta&lt;/a&gt;. I&apos;m being more careful about keeping my work laptop (which has also become my main dev machine) in good working order, so I&apos;ll be installing the SP1 beta on a separate VM just to be safe.&lt;/p&gt;
&lt;p&gt;I&apos;ll do a follow-up post with my experiences. It might take a few days, as tomorrow I&apos;m off riding in the Tour Down Under (again!), and might need a day or two to recover!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2009/07/art-of-unit-testing-with-examples-in</id>
    <updated>2009-07-14T22:49:00.001+09:30</updated>
    <title>The art of Unit Testing with Examples in .NET</title>
    <link href="https://david.gardiner.net.au/2009/07/art-of-unit-testing-with-examples-in" rel="alternate" type="text/html" title="The art of Unit Testing with Examples in .NET"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <category term="Books"/>
    <published>2009-07-14T22:49:00.001+09:30</published>
    <summary type="html">The Art of Unit Testing: with Examples in .NET by Roy Osherove</summary>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Art-Unit-Testing-Examples-NET/dp/1933988274%3FSubscriptionId%3D0JTCV5ZMHMF7ZYTXGFR2%26tag%3Ddavesdayd-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D1933988274&quot;&gt;The Art of Unit Testing: with Examples in .NET&lt;/a&gt; by Roy Osherove&lt;/p&gt;
&lt;p&gt;I recently bought a copy of &lt;a href=&quot;https://www.amazon.com/Art-Unit-Testing-Examples-NET/dp/1933988274%3FSubscriptionId%3D0JTCV5ZMHMF7ZYTXGFR2%26tag%3Ddavesdayd-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D1933988274&quot;&gt;The Art of Unit Testing: with Examples in .NET&lt;/a&gt; by Roy Osherove, taking advantage of a discount being offered by Manning.&lt;/p&gt;
&lt;p&gt;Already owning &lt;a href=&quot;https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054%3FSubscriptionId%3D0JTCV5ZMHMF7ZYTXGFR2%26tag%3Ddavesdayd-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0131495054&quot;&gt;xUnit Test Patterns: Refactoring Test Code (Addison-Wesley Signature Series)&lt;/a&gt; by Gerard Meszaros and &lt;a href=&quot;https://www.amazon.com/Working-Effectively-Legacy-Robert-Martin/dp/0131177052%3FSubscriptionId%3D0JTCV5ZMHMF7ZYTXGFR2%26tag%3Ddavesdayd-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0131177052&quot;&gt;Working Effectively with Legacy Code (Robert C. Martin Series)&lt;/a&gt; by Michael Feathers, I was interested to see what new insights Osherove would bring, especially as he would be focussing purely on the .NET platform (code samples are in C#).&lt;/p&gt;
&lt;p&gt;The book is divided into 4 sections.&lt;/p&gt;
&lt;p&gt;Part 1 (which I skimmed through) was an introduction to unit testing. Osherove uses &lt;a href=&quot;http://www.nunit.org&quot;&gt;NUnit&lt;/a&gt; for his examples which is reasonable considering it is probably the most popular unit testing framework for .NET at the moment.&lt;/p&gt;
&lt;p&gt;His preferred naming convention for test methods is interesting – &quot;[MethodUnderTest]_[Scenario]_[ExpectedBehaviour]&quot;. It contrasts with the &quot;natural sentence&quot; style I currently favour (which I&apos;d trace back to &lt;a href=&quot;https://web.archive.org/web/20240327132504/http://blog.jpboodhoo.com/&quot;&gt;Jean-Paul Boodhoo&lt;/a&gt;&apos;s unit testing episodes on &lt;a href=&quot;https://www.dnrtv.com/&quot;&gt;dnrTV&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;He also recommends naming test classes with a &quot;Tests&quot; suffix. I&apos;ve always used the singular &quot;Test&quot;, but I take his point that each test class does contain multiple tests so plural may be more accurate.&lt;/p&gt;
&lt;p&gt;Part 2 introduces stubs, mock objects and isolation frameworks (aka mock object frameworks). Following a &lt;a href=&quot;https://web.archive.org/web/20130531154133/http://weblogs.asp.net/rosherove/archive/2007/04/26/choosing-a-mock-object-framework.aspx&quot;&gt;poll held on Osherove&apos;s blog&lt;/a&gt;, &lt;a href=&quot;https://ayende.com/blog/&quot;&gt;Rhino Mocks&lt;/a&gt; was chosen to demonstrate how a framework can simplify creating mock objects. I could be wrong, but you almost get a sense that this was done somewhat grudgingly considering Osherove himself works for &lt;a href=&quot;https://www.typemock.com/&quot;&gt;TypeMock&lt;/a&gt; (who sell a commercial mocking framework). Curiously whilst including a link to the Rhino Mocks download site, he doesn&apos;t even mention Ayende&apos;s name at all.&lt;/p&gt;
&lt;p&gt;Osherove makes the following recommendations about stubs and mocks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use nonstrict mocks when you can&lt;/li&gt;
&lt;li&gt;Use stubs instead of mocks when you can&lt;/li&gt;
&lt;li&gt;Avoid using stubs as mocks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I found this interesting, as we&apos;ve been writing a lot of unit tests lately, and one of the things we&apos;re coming to realise is that brittle tests can be annoying. Tests that still test your code, but are more flexible about exactly how the code under test works (rather than setting strict expectations for each and every method call) are going to be more useful and less effort to maintain.&lt;/p&gt;
&lt;p&gt;Part 3 looks at various strategies for organising tests and a number of patterns and anti-patterns to follow when creating tests.&lt;/p&gt;
&lt;p&gt;Part 4 covers how to make unit testing the norm in an organisation, and how to work with legacy code.&lt;/p&gt;
&lt;p&gt;Having some experience writing unit tests, I did find this book a bit light on. Osherove references Meszaros and Feather&apos;s books regularly. I would also consider both of these works cover the topic in a more detailed and thorough manner. However they are probably not necessarily as good a starting point for someone new to unit testing, especially someone who&apos;s main experience lies in developing for the .NET platform.&lt;/p&gt;
&lt;p&gt;I finished the book surprisingly quickly – pleased that I&apos;d learned a few new things, but left feeling that it would have been nice to learn a few more.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2009/06/tracing-and-logging-wpf</id>
    <updated>2009-06-22T08:00:00.000+09:30</updated>
    <title>Tracing and logging WPF</title>
    <link href="https://david.gardiner.net.au/2009/06/tracing-and-logging-wpf" rel="alternate" type="text/html" title="Tracing and logging WPF"/>
    <category term=".NET"/>
    <category term="Unit Testing"/>
    <published>2009-06-22T08:00:00.000+09:30</published>
    <summary type="html">Whilst researching unit testing WPF applications I came across Bob King&apos;s answer to this question on StackOverflow.</summary>
    <content type="html">&lt;p&gt;Whilst researching unit testing WPF applications I came across &lt;a href=&quot;https://web.archive.org/web/20231202002438/https://stackoverflow.com/questions/331215/unit-test-wpf-bindings&quot;&gt;Bob King&apos;s answer to this question on StackOverflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;WPF DataBinding can appear to be a bit of a mysterious black box, but I now know that you can enable diagnostic &lt;a href=&quot;https://learn.microsoft.com/dotnet/api/system.diagnostics.presentationtracesources?view=windowsdesktop-10.0&quot;&gt;trace sources&lt;/a&gt; to see what&apos;s happening under the covers. Mike Hillberg shows some examples in &lt;a href=&quot;http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
