<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Brian Schroer</title>
    <description>The latest articles on DEV Community by Brian Schroer (@sparky).</description>
    <link>https://dev.to/sparky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F217627%2F58a11153-2331-4c99-8183-04d6b0ce7186.jpg</url>
      <title>DEV Community: Brian Schroer</title>
      <link>https://dev.to/sparky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sparky"/>
    <language>en</language>
    <item>
      <title>Regular expression capture and replace examples</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Mon, 26 Sep 2022 01:20:45 +0000</pubDate>
      <link>https://dev.to/sparky/regular-expression-capture-and-replace-examples-44o5</link>
      <guid>https://dev.to/sparky/regular-expression-capture-and-replace-examples-44o5</guid>
      <description>&lt;p&gt;&lt;em&gt;Regular expressions are incredibly powerful, but they're sometimes described as "looking like cartoon characters swearing", and the syntax can be difficult to remember.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I only find myself needing to write code to "capture" and replace values from a string a couple of times a year and always have to re-learn the syntax, so I'm blogging this for my reference. I hope it will be helpful to you also.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm including examples in JavaScript and C#...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the scenario for the following examples: We want to log XML payment account setup requests/responses, but the request contains a bank account number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;Sparky's Bank Account&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;BankRoutingNumber&amp;gt;&lt;/span&gt;123456789&lt;span class="nt"&gt;&amp;lt;/BankRoutingNumber&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;BankAccountNumber&amp;gt;&lt;/span&gt;987654321&lt;span class="nt"&gt;&amp;lt;/BankAccountNumber&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bank account numbers are sensitive information, so we don't want to log them as-is.&lt;/p&gt;

&lt;p&gt;Here's the basic Regex to find BankAccountNumber XML elements. The "\d+" (one or more digits) pattern defines the account number:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;BankAccountNumber&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;d+&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;/BankAccountNumber&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;@"&amp;lt;BankAccountNumber&amp;gt;\d+&amp;lt;\/BankAccountNumber&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Compiled&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and code using the Regex to find the bank account XML elements:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Results:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;BankAccountNumber&amp;gt;987654321&amp;lt;/BankAccountNumber&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Replacing
&lt;/h1&gt;

&lt;p&gt;Let's replace the bank account numbers with asterisks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;censored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;BankAccountNumber&amp;gt;*********&amp;lt;/BankAccountNumber&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;censored&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;censored&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;BankAccountNumber&amp;gt;*********&amp;lt;/BankAccountNumber&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;censored&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Results:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Name&amp;gt;Sparky's Bank Account&amp;lt;/Name&amp;gt;
&amp;lt;BankRoutingNumber&amp;gt;123456789&amp;lt;/BankRoutingNumber&amp;gt;
&amp;lt;BankAccountNumber&amp;gt;*********&amp;lt;/BankAccountNumber&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Capturing
&lt;/h1&gt;

&lt;p&gt;A capture group is defined by enclosing part of the regex ("\d+" in this example) in parentheses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;BankAccountNumber&amp;gt;(\\d+)&amp;lt;\/BankAccountNumber&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire match (the XML element in this example) is an automatic capture group, so this will result in two capture groups:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;captures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Results:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0] &amp;lt;BankAccountNumber&amp;gt;987654321&amp;lt;/BankAccountNumber&amp;gt;
[1] 987654321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Named captures
&lt;/h1&gt;

&lt;p&gt;You can name a capture group with the syntax "(?&amp;lt;&lt;em&gt;name&lt;/em&gt;&amp;gt;&lt;em&gt;pattern&lt;/em&gt;)". In this example, I'm using the name "acctNum":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;BankAccountNumber&amp;gt;(?&amp;lt;acctNum&amp;gt;\\d+)&amp;lt;\/BankAccountNumber&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;em&gt;The angle bracket syntax for group naming is a bit confusing for this example because it looks like XML.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Using a named capture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acctNum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"acctNum"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Results:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;987654321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Replacing with a function
&lt;/h1&gt;

&lt;p&gt;We shouldn't log account numbers, but let's say our security policy allows logging them "masked". Let's try replacing with a "callback" function that replaces all but the last four digits with asterisks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;censored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;innerMatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acctNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;innerMatches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acctNum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acctNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;masked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;acctNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;BankAccountNumber&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;masked&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/BankAccountNumber&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;censored&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;censored&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestXml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accountNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"acctNum"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accountNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;masked&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;accountNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"&amp;lt;BankAccountNumber&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;masked&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/BankAccountNumber&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Results:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Amount&amp;gt;123.45&amp;lt;/Amount&amp;gt;
&amp;lt;BankRoutingNumber&amp;gt;123456789&amp;lt;/BankRoutingNumber&amp;gt;
&amp;lt;BankAccountNumber&amp;gt;*****4321&amp;lt;/BankAccountNumber&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;a href="https://dotnetfiddle.net/kuFUfE"&gt;.Net "Fiddle" containing the C# code excerpted above&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jsfiddle.net/SparkySchroer/vtf3sxek/94/"&gt;JS "Fiddle" containing the JavaScript code excerpted above&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>regex</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: LINQPad</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Wed, 10 Aug 2022 14:22:00 +0000</pubDate>
      <link>https://dev.to/sparky/sparkys-tool-tips-linqpad-3o0b</link>
      <guid>https://dev.to/sparky/sparkys-tool-tips-linqpad-3o0b</guid>
      <description>&lt;p&gt;Of all my favorite tools, LINQPad is probably &lt;strong&gt;&lt;em&gt;the&lt;/em&gt;&lt;/strong&gt; favorite. I use it every day, several times per day.&lt;/p&gt;

&lt;h2&gt;
  
  
  LINQ
&lt;/h2&gt;

&lt;p&gt;In 2007, Microsoft's .NET 3.5 introduced &lt;a href="https://en.wikipedia.org/wiki/Language_Integrated_Query"&gt;(L)anguage (In)tegrated (Q)uery&lt;/a&gt;, one of the killer features of .NET, and Joseph Albahari introduced LINQPad almost immediately afterward, providing the ability to do database queries using LINQ as an alternative to SQL.&lt;/p&gt;

&lt;p&gt;Here's a screenshot from an early version of LINQPad (it still pretty much looks the same):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JdgAlto4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/425cb53xrnsc09dmhi7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JdgAlto4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/425cb53xrnsc09dmhi7i.png" alt="LINQPad version 1 screenshot" width="880" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above uses LINQ "query syntax", but of course, you can also use "method syntax" (.Where, .OrderBy). That's because LINQPad isn't just for LINQ and databases - It's for any any C#/F#/VB expression, statement block or program...&lt;/p&gt;

&lt;h2&gt;
  
  
  Code "scratchpad"
&lt;/h2&gt;

&lt;p&gt;If you want to quickly try out some code, instead of creating a console app in Visual Studio, you can just open up LINQPad and and type a line or two...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qFXx3-XA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uz781jrcph7xmd71c1j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qFXx3-XA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uz781jrcph7xmd71c1j5.png" alt="LINQPad as a code scratchpad" width="437" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use it a lot for quick code experimentation, and have amassed a collection of tiny utility "program" queries in LINQPad (JSON and XML formatting, removing line feeds and tabs from strings, etc.), but working with databases is LINQPad's main claim to fame...&lt;/p&gt;

&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;As shown in the animation below that I "borrowed" from &lt;a href="https://developers.mews.com/linqpad-the-virtual-scratchpad-you-never-knew-you-wanted/"&gt;Jiří Petržilka's blog&lt;/a&gt;, if you query a table with foreign keys, LINQPad shows those relationships as navigation links to those entities:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--srNposP2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://developers.mews.com/wp-content/uploads/NavigationProperty-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--srNposP2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://developers.mews.com/wp-content/uploads/NavigationProperty-2.gif" alt="Navigation" width="880" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Querying with LINQ is what LINQPad was created for, but you can also query with plain old SQL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7p0DHfGm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/576d8txr25n7vwwkcm73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7p0DHfGm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/576d8txr25n7vwwkcm73.png" alt="LINQPad SQL query" width="880" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do query with LINQ, you can click the results "SQL" tab to see the SQL that was generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fSe1oOz5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5c6ksh8kzy82mhxyj59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fSe1oOz5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5c6ksh8kzy82mhxyj59.png" alt="Generated SQL" width="848" height="214"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I almost never use SQL Server Management Studio for SQL queries any more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions, references &amp;amp; NuGet
&lt;/h2&gt;

&lt;p&gt;You can reference .NET DLLs and NuGet packages and use them in your queries and create/save your own extension methods and classes and use them in your queries:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ktJllWMA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tzix9hferp1pwexxcr3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ktJllWMA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tzix9hferp1pwexxcr3w.png" alt='"My Extensions"' width="880" height="526"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In this example query, the underlined code uses extensions I wrote to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run the SQL Server query "WITH (NOLOCK)" (in a TransactionScope with IsolationLevel.ReadUncommitted, actually) so it doesn't tie up the table&lt;/li&gt;
&lt;li&gt;convert from UTC time to my local time&lt;/li&gt;
&lt;li&gt;format a column containing JSON with indentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SqMQzWyO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1rignh37d0bpvqjp58u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SqMQzWyO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1rignh37d0bpvqjp58u.png" alt="LINQPad query using extensions" width="683" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;There is a free version, which I'm ashamed to admit I used for probably thousands of hours before finally doing the right thing and sending some money Joseph's way.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://www.linqpad.net/Purchase.aspx"&gt;several premium license versions&lt;/a&gt; starting at US $59, which add autocompletion &amp;amp; tooltips, code formatting, cross-database querying, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I'm afraid this post doesn't adequately convey LINQPad's awesomeness. I've been using it for years, and keep discovering new capabilities (interactive regular expression editing &amp;amp; testing !?). &lt;/p&gt;

&lt;p&gt;I urge you to give it a try.&lt;/p&gt;




&lt;p&gt;You can learn more about LINQPad and download the installer from&lt;br&gt;
&lt;a href="https://www.linqpad.net/"&gt;https://www.linqpad.net/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: "Favorite Documents" Visual Studio extension</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Thu, 29 Jul 2021 02:23:58 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-favorite-documents-visual-studio-extension-35</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-favorite-documents-visual-studio-extension-35</guid>
      <description>&lt;p&gt;When you're working with a Visual Studio solution, even if it contains multiple projects and dozens of files, you usually spend most of your time working with a handful of documents.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://marketplace.visualstudio.com/items?itemName=SergeyVlasov.FavoriteDocuments" rel="noopener noreferrer"&gt;Favorite Documents&lt;/a&gt; Visual Studio 2019 extension adds a "Favorite Documents" menu to Visual Studio to make it easy to get to them.&lt;/p&gt;

&lt;p&gt;When you're working with a file that you want to get back to easily later, just click the "Favorite Documents" toolbar's "⭐" icon:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttj10wstuipsu1u6hsqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttj10wstuipsu1u6hsqo.png" alt="Favorite Documents "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...and click the popup dialog's "Add" button to add it to the favorites menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnx4rdiprt3l5ix09k8o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnx4rdiprt3l5ix09k8o.png" alt="Favorite Documents menu with file added"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screenshot, I've added several favorite documents:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff67jhqtrzz4jzi263dl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff67jhqtrzz4jzi263dl0.png" alt="Favorite Documents menu with multiple files"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second, "eight-pointed star" icon adds all open documents to your Favorite Documents, and the third icon can be used to add external documents.&lt;/p&gt;

&lt;p&gt;Favorites are "remembered", so if you close a solution and come back to it later, the favorites will still be there.&lt;/p&gt;

&lt;p&gt;By default, favorites are "Solution-specific" - they're tied to the solution's folder, so if you open another branch of the same solution you won't see the favorites.&lt;/p&gt;

&lt;p&gt;If you don't check the "Solution-specific" checkbox when adding, the favorites are relative to the solution root, so they work across multiple branches. Here are my cross-solution favorites:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm9ib5y70dbyvny5wl5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm9ib5y70dbyvny5wl5w.png" alt="Favorite Documents menu with cross-solution favorites"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things I'd like to point out in the screenshot above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can add Source Control Explorer links as favorites&lt;/li&gt;
&lt;li&gt;You can organize favorites in folders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last icon on the Favorite Documents toolbar brings up the "Organize Favorites" window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F134eqzar6kjaxpn52yvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F134eqzar6kjaxpn52yvc.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>visualstudio</category>
      <category>dotnet</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: TFS Auto Shelve</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Mon, 26 Jul 2021 21:02:40 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-tfs-auto-shelve-48ig</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-tfs-auto-shelve-48ig</guid>
      <description>&lt;p&gt;This may be the most useful tool I've blogged about, and probably the simplest...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Vercellone.TFSAutoShelveforVisualStudio"&gt;TFS Auto Shelve for Visual Studio&lt;/a&gt; is a Visual Studio 2019 extension that periodically shelves pending changes to Team Foundation System or Azure DevOps source control.&lt;/p&gt;

&lt;p&gt;I can't count how many times I've used it to retrieve code files I've accidentally deleted, or files where I've started making changes and saved my mind and wanted to "roll back" to a previous version that hadn't been checked in.&lt;/p&gt;

&lt;p&gt;You use the Visual Studio "Tools | Options | TFS Auto Shelve" menu to configure how often you want to shelve, how many shelvesets to keep, and how they should be named.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tk61Nlh7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5yoda7xwuz6h9rz3jdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tk61Nlh7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5yoda7xwuz6h9rz3jdx.png" alt="TFS Auto Shelve Options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The name is defined using placeholders, and the default name is "Auto-{0}":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;{0}: WorkspaceInfo.Name (machine name for me - I'm not sure if it's something different for other installations)&lt;/li&gt;
&lt;li&gt;{1}: WorkspaceInfo.OwnerName&lt;/li&gt;
&lt;li&gt;{2}: DateTime.Now&lt;/li&gt;
&lt;li&gt;{3}: Domain of WorkspaceInfo.OwnerName&lt;/li&gt;
&lt;li&gt;{4}: UserName of WorkspaceInfo.OwnerName&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can retrieve shelved files via the Visual Studio "File | Source Control | Find | Find Shelvesets" menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J0bKi2s---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1llp2r568p84ai3whgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J0bKi2s---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1llp2r568p84ai3whgi.png" alt='"Find Shelvesets" window, showing shelvesets saved by TFS Auto Shelve'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(I've used my incredible graphic skills😉 on the screenshot to block out information specific to the company where I work.)&lt;/p&gt;

&lt;p&gt;You can see from the screenshot above that there were no shelvesets created overnight or over the weekend. Iit's smart enough to only create them while you're actively using Visual Studio. &lt;/p&gt;

&lt;p&gt;The extension adds two items to the Visual Studio "Team" menu. The first shows you whether the extension is running, and the second lets you create an on-demand shelveset:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvBrjS6f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w96tachr1q6wyw3k0kvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvBrjS6f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w96tachr1q6wyw3k0kvs.png" alt="Teams / TFS Auto Shelve Visual Studio menu"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tfs</category>
      <category>sourcecontrol</category>
      <category>dotnet</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: Moq.AutoMock</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Mon, 26 Jul 2021 01:09:27 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-moq-automock-3ka3</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-moq-automock-3ka3</guid>
      <description>&lt;p&gt;Probably the most important thing that makes .NET classes unit-testable is &lt;a href="https://stackify.com/dependency-injection-c-sharp/"&gt;dependency injection&lt;/a&gt; (injecting &lt;a href="https://betterprogramming.pub/code-against-interfaces-not-implementations-37b30e7ab992"&gt;interfaces, not implementations&lt;/a&gt;), usually via constructor injection.&lt;/p&gt;

&lt;p&gt;Mocking libraries make it easier to generate "mock" interface implementations, and to verify that the interfaces were used as expected. &lt;a href="https://github.com/moq/moq4"&gt;Moq&lt;/a&gt; is the most popular .NET mocking library. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you're using Moq, you may be interested in my &lt;a href="https://dev.to/sparky/sparkytesthelpers-moq-307h"&gt;SparkyTestHelpers.Moq and SparkyTestHelpers.MoqFluent&lt;/a&gt; NuGet packages. They provide an alternative to "It.IsAny" and "It.Is" syntax that I think is easier to write and read, and &lt;a href="https://fluentassertions.com/"&gt;fluent assertions&lt;/a&gt; for verifications.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;....lost of interfaces&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.nuget.org/packages/Moq.AutoMock/"&gt;Moq.AutoMock NuGet package&lt;/a&gt; provides an "auto-mocker" container for Moq.&lt;/p&gt;

&lt;p&gt;AutoMocker.CreateInstance&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; creates a new instance of the specified type, automatically generating Mocks for all of its constructor interface parameters. (The class must have a constructor where &lt;em&gt;all&lt;/em&gt; of the parameters are interfaces):&lt;/p&gt;

&lt;p&gt;Without AutoMocker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mockReader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IReader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mockFormatter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IFormatter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mockLogger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;mockReader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mockFormatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mockLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With AutoMocker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;autoMocker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AutoMocker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;autoMocker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To &lt;strong&gt;.Setup&lt;/strong&gt; or &lt;strong&gt;.Verify&lt;/strong&gt; mock behavior, you can get the auto-generated mock via AutoMocker's "GetMock&amp;lt;&lt;em&gt;TMock&lt;/em&gt;&amp;gt;" method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;autoMocker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IFormatter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"###.00"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may find it advantageous to create a custom AutoMocker subclass for your project that sets up "happy path" behavior for some of your commonly used interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomAutoMocker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AutoMocker&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// constructor&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomAutoMocker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set up commonly used interfaces&lt;/span&gt;
        &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IFormatter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mockFormatter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetMock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IFormatter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;//mockFormatter.Setup(x =&amp;gt; ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;autoMocker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CustomAutoMocker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="n"&gt;autoMocker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// following tests will use Setups defined in the CustomAutoMocker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A custom AutoMocker subclass can also override the &lt;strong&gt;CreateInstance&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&lt;/strong&gt; method to perform additional logic when an instance of a specific type is created. For example, in a project using &lt;strong&gt;ApiController&lt;/strong&gt;s that I work on (which was not originally written with unit testing in mind), I detect construction of &lt;strong&gt;ApiController&lt;/strong&gt; instances and automatically set up their &lt;strong&gt;.Request&lt;/strong&gt; and &lt;strong&gt;.User&lt;/strong&gt; properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomAutoMocker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AutoMocker&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiController&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set up ApiController's Request and User &lt;/span&gt;
        &lt;span class="n"&gt;apiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpRequestMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;apiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpConfiguration&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;apiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ClaimsPrincipal&lt;/span&gt; &lt;span class="nf"&gt;BuildClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// (code to build ClaimsPrincipal and&lt;/span&gt;
    &lt;span class="c1"&gt;// assign it to Thread.CurrentPrincipal)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Mock.AutoMock&lt;/strong&gt; and a custom &lt;strong&gt;AutoMocker&lt;/strong&gt; subclass have greatly reduced the amount of repetitive and tedious mock setup code I need to write in my unit tests.&lt;/p&gt;

&lt;p&gt;If you're using &lt;strong&gt;Moq&lt;/strong&gt;, I highly recommend it.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: Customize Visual Studio Window Title /  SolutionColor</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Wed, 21 Jul 2021 11:50:20 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-customize-visual-studio-window-title-solutioncolor-54di</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-customize-visual-studio-window-title-solutioncolor-54di</guid>
      <description>&lt;p&gt;If you're like me, you usually have multiple Visual Studio solutions open at the same time.&lt;/p&gt;

&lt;p&gt;The main application I work on is an ASP.NET web site with a separate API back end site, so I usually have the web solution running in one Visual Studio instance and the API solution in another. &lt;/p&gt;

&lt;p&gt;I also frequently work on multiple feature branches at once, so it's not uncommon for me to have three or four Visual Studio instances going.&lt;/p&gt;

&lt;p&gt;When you do that, it can be confusing to tell which instance is which. These are two Visual Studio 2019 extensions that help...&lt;/p&gt;

&lt;h1&gt;
  
  
  SolutionColor
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Wumpf.SolutionColor"&gt;https://marketplace.visualstudio.com/items?itemName=Wumpf.SolutionColor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one's really simple. It just adds a toolbar with buttons you can use to set the color of the title bar on a per-solution basis. Your color choice is "remembered" and re-applied every time you open the solution:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xvoH5Wxu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncvv0v9qjye7lr1u1kiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xvoH5Wxu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncvv0v9qjye7lr1u1kiw.png" alt='"SolutionColor" extension toolbar buttons"'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Pick title bar color" button pops up a color picker to choose a background color. A foreground color of black or white is automatically chosen - whichever is most readable against the background:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--emldxLUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlvdadpbrh5j4j8516th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--emldxLUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlvdadpbrh5j4j8516th.png" alt='"SolutionColor" extension color picker'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I usually choose a bright color for the web solution (since it's the up-front pretty solution), and a darker version of that color for the API solution:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ibDtSgYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ci6pga0dd6vxuqrgmv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ibDtSgYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ci6pga0dd6vxuqrgmv2.png" alt="Two Visual Studio windows with different title bar colors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Customize Visual Studio Window Title
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mayerwin.RenameVisualStudioWindowTitle"&gt;https://marketplace.visualstudio.com/items?itemName=mayerwin.RenameVisualStudioWindowTitle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension, as you can probably guess from its name, customizes the Visual Studio window title. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unlike previous Visual Studio versions, VS 2019 by default doesn't &lt;em&gt;have&lt;/em&gt; a title bar in the app window. (You can enable it via the "Tools | Options | Environment | Preview Features | Use compact menu and search bar" menu.) The title is also displayed on Windows taskbar buttons, though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's the default taskbar button title experience:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S0BqgyYi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8xdq03zjjp6o41cvggv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S0BqgyYi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8xdq03zjjp6o41cvggv.png" alt="Boring default Visual Studio taskbar title"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The extension allows you to use "tags" (e.g. "[solutionName]", "[parentPath]") to define how titles should be formatted:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Efejh0hL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccfqo0cy980sasijjrq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Efejh0hL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccfqo0cy980sasijjrq8.png" alt='"Customize Visual Studio Window Title" options: Global Rules'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a &lt;em&gt;lot&lt;/em&gt; of tags you can use, and they're defined in the "Supported Tags" option window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PtSPCe7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilmzqhoylbklwthue4hq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PtSPCe7T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilmzqhoylbklwthue4hq.png" alt='"Customize Visual Studio Window Title" options: Supported Tags'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can build different titles for design mode, running mode and break mode. I like to use "|" between solution name and parent path in design mode and emojis to simulate video player "play" and "pause" buttons in running and break modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;design: [solutionName] | [parentPath]&lt;/li&gt;
&lt;li&gt;running: [solutionName] ▶ [parentPath]&lt;/li&gt;
&lt;li&gt;break: [solutionName] ⏸ [parentPath}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to these default title formulas, you can set up titles for specific solutions, saved to either a file that lives with that solution or to a central "global" XML file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EMcALTU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckun9z25bz1k0ll0pqju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EMcALTU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckun9z25bz1k0ll0pqju.png" alt='"Customize Visual Studio Window Title" options: Solution Specific'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I prefer the "global config". The global XML file contains "SolutionSet" elements where you define solution folders and names, and the title formulas to use for each:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TNYo3xqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uox45asplo8x6dv7dlxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TNYo3xqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uox45asplo8x6dv7dlxp.png" alt='"Customize Visual Studio Window Title" global configuration XML file'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the results on my Windows taskbar. I can easily see that I have an API solution and web solution instance open, whether they're running or not, and the branch name for each:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a5DRz5pt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/344w445ttqq3d5fm119z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a5DRz5pt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/344w445ttqq3d5fm119z.png" alt="Customized taskbar titles"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: Fancy Zones</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Fri, 16 Jul 2021 23:13:05 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-fancy-zones-566</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-fancy-zones-566</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Not strictly a developer tool, but devs usually have lots of app windows open at the same time, and I'm sure you'll find this useful.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;...and when I say "windows" I mean Windows windows. There's probably something similar in the macOS world, but the last Apple product I used was a ][+, so I can't give you any advice about that.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Windows 10 has extremely basic support for window dragging and "snapping". If you drag a window off the side of the screen, you'll see a transparent overlay over that horizontal half of the screen and if you "drop" the window, it will snap/dock to fill half of the screen.&lt;/p&gt;

&lt;p&gt;Fancy Zones, one of the &lt;a href="https://docs.microsoft.com/en-us/windows/powertoys/"&gt;Microsoft PowerToys utilities&lt;/a&gt;, lets you do something, well... fancier.&lt;/p&gt;

&lt;p&gt;The Fancy Zones layout editor lets you define "drop zones". There are several default templates, but if you want to get "fancy", you create a custom layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iUIvDri7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xh4ivab35y2mgyana720.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iUIvDri7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xh4ivab35y2mgyana720.png" alt="Fancy Zones editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have multiple monitors, you can set up different layouts on each.&lt;/p&gt;

&lt;p&gt;When you're dragging a windows and press the Shift key, the drop zones light up, and if you drop the window onto a zone, it will snap to fill the zone. Here it is in action on my machine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I0VjPKKQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adylc5xx4wj4xwcf4qmo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I0VjPKKQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adylc5xx4wj4xwcf4qmo.jpg" alt="Fancy Zones drop zones"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A developer can't be expected to work with a measly five screens😏, so I've got my wide monitors set up with side-by-side zones and my "portrait" left-side monitor set up with top and bottom zones so I can easily rock eight or nine docked windows at a time.&lt;/p&gt;

&lt;p&gt;Fancy!&lt;/p&gt;




&lt;p&gt;PowerToys also has a lot of other goodies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Color Picker&lt;/li&gt;
&lt;li&gt;File Explorer add-ons&lt;/li&gt;
&lt;li&gt;Image Resizer&lt;/li&gt;
&lt;li&gt;Keyboard Manager&lt;/li&gt;
&lt;li&gt;PowerRename&lt;/li&gt;
&lt;li&gt;PowerToys Run (a mini version of &lt;a href="http://www.wox.one/"&gt;Wox&lt;/a&gt; - spoiler alert: Wox will be the subject of one of my future "favorite tools" posts.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;P.S. I forgot to mention the price - There ain't one - It's free!&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>windows</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: NimbleText</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Wed, 14 Jul 2021 02:46:49 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-nimbletext-55bm</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-nimbletext-55bm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;NimbleText is a Windows application, but if you're not on Windows or don't want to install something on your machine, &lt;a href="https://nimbletext.com/Live"&gt;there's an online version&lt;/a&gt; (albeit with fewer features).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been using NimbleText for about 15 years. (Actually, I started with Leon Bambrick's web site "&lt;a href="https://secretgeek.net/programmers_mate"&gt;The World's Simplest Code Generator&lt;/a&gt;", which eventually morphed into NimbleText.&lt;/p&gt;

&lt;p&gt;The idea behind NimbleText is simple. You give it a delimited list of data and a substitution pattern, and it outputs resulting rows. Here's an animation from the NimbleText web site showing it being used to generate SQL statements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZUNeQhKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://nimbletext.com/images/NTAnimSQL.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZUNeQhKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://nimbletext.com/images/NTAnimSQL.gif" alt="Animated gif showing how NimbleText works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Functions
&lt;/h3&gt;

&lt;p&gt;That's enough for most of the text manipulation I need to do, but there are also several column manipulation functions that can be inserted via its "Functions" menu:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;toUpperCase&lt;/li&gt;
&lt;li&gt;toLowerCase&lt;/li&gt;
&lt;li&gt;toTitleCase&lt;/li&gt;
&lt;li&gt;toPascalCase&lt;/li&gt;
&lt;li&gt;toCamelCase&lt;/li&gt;
&lt;li&gt;toSentenceCase&lt;/li&gt;
&lt;li&gt;toWords&lt;/li&gt;
&lt;li&gt;reverse&lt;/li&gt;
&lt;li&gt;replace&lt;/li&gt;
&lt;li&gt;htmlEncode&lt;/li&gt;
&lt;li&gt;htmlDecode&lt;/li&gt;
&lt;li&gt;urlEncode&lt;/li&gt;
&lt;li&gt;urlDecode&lt;/li&gt;
&lt;li&gt;xmlEncode&lt;/li&gt;
&lt;li&gt;xmlDecode&lt;/li&gt;
&lt;li&gt;split&lt;/li&gt;
&lt;li&gt;charAt&lt;/li&gt;
&lt;li&gt;indexOf&lt;/li&gt;
&lt;li&gt;lastIndexOf&lt;/li&gt;
&lt;li&gt;slice&lt;/li&gt;
&lt;li&gt;substr&lt;/li&gt;
&lt;li&gt;left&lt;/li&gt;
&lt;li&gt;right&lt;/li&gt;
&lt;li&gt;lpad&lt;/li&gt;
&lt;li&gt;rpad&lt;/li&gt;
&lt;li&gt;dateFormat&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's how much I rely on NimbleText - I just reflexively &lt;em&gt;used&lt;/em&gt; the tool to help write this post &lt;em&gt;about&lt;/em&gt; the tool. I used it to combine dashes and function names to create the Markdown for that bulleted list of functions!😲&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can save/load data and patterns to/from local files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing
&lt;/h3&gt;

&lt;p&gt;It's free!&lt;/p&gt;

&lt;p&gt;You can purchase a license for $19.95 USD that unlocks some additional features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"where" filters&lt;/li&gt;
&lt;li&gt;sorting&lt;/li&gt;
&lt;li&gt;snippet management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used NimbleText and other similarly priced tools for years for free before realizing that it's a pretty small price to pay for something so valuable. Don't be like me - If there's a tool that makes your life easier, throw the developers a few bucks.&lt;/p&gt;




&lt;p&gt;While writing this post, I came across some things I didn't know (or had forgotten) that NimbleText can do. I'm looking forward to trying them out.&lt;/p&gt;

&lt;p&gt;Give NimbleText a try, I think you'll like it!&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>codegeneration</category>
      <category>windows</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Sparky's Tool Tips: Fluent Assertions</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Sun, 11 Jul 2021 14:20:53 +0000</pubDate>
      <link>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-fluent-assertions-1a5o</link>
      <guid>https://dev.to/sparky/these-are-a-few-of-my-favorite-tools-fluent-assertions-1a5o</guid>
      <description>&lt;p&gt;I like writing unit tests more than the average developer (not saying much, I realize - Testing is something many devs... de-&lt;em&gt;test&lt;/em&gt; 🥁).&lt;/p&gt;

&lt;p&gt;I really appreciate tools that make it easier and more fun to write tests (in fact, &lt;a href="https://dev.to/sparky/sparkytesthelpers-1n0h"&gt;I've written some myself&lt;/a&gt;), and &lt;a href="https://fluentassertions.com/" rel="noopener noreferrer"&gt;Fluent Assertions&lt;/a&gt; is one of my favorites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffluentassertions.com%2Fassets%2Fimages%2Ffluent_assertions_large_horizontal.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffluentassertions.com%2Fassets%2Fimages%2Ffluent_assertions_large_horizontal.svg" alt="Fluent Assertions logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting" rel="noopener noreferrer"&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/a&gt; &lt;strong&gt;Assert&lt;/strong&gt;, &lt;strong&gt;StringAssert&lt;/strong&gt; and &lt;strong&gt;CollectionAssert&lt;/strong&gt; classes are... &lt;em&gt;OK&lt;/em&gt;, I guess. (It's been a while since I used NUnit and I've never used xUnit, so I don't know about the built-in asserts in those frameworks.)&lt;/p&gt;

&lt;p&gt;Fluent Assertions is a set of .NET extension methods that allow you to specify the expected outcome of a TDD or BDD-style unit test with a sentence-like syntax. For example, the "MSTest" syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daysInJune&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...can be fluently expressed as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FluentAssertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;daysInJune&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Using" the "FluentAssertions" namespace enables a ton of useful "Should()" extension methods for:&lt;/p&gt;

&lt;h4&gt;
  
  
  strings:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Be(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;BeNull()&lt;/li&gt;
&lt;li&gt;BeEmpty()&lt;/li&gt;
&lt;li&gt;StartWith(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;EndWith(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;HaveLength(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Contain(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  numbers:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Be(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;BeGreatorOrEqualTo(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;BeLessOrEqualTo(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;BeInRange(&lt;em&gt;minimum&lt;/em&gt;, &lt;em&gt;maximum&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;BePositive()&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  DateTime:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Be(1.March(2022).At(22, 15))&lt;/li&gt;
&lt;li&gt;BeOnOrAfter(1.March(2010))&lt;/li&gt;
&lt;li&gt;HaveDay(1)&lt;/li&gt;
&lt;li&gt;HaveMonth(3)&lt;/li&gt;
&lt;li&gt;HaveYear(2010)&lt;/li&gt;
&lt;li&gt;HaveHour(22)&lt;/li&gt;
&lt;li&gt;BeLessThan(10.Minutes()).Before(&lt;em&gt;otherDatetime&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  collections:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;HaveCount(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;HaveSameCount(&lt;em&gt;otherCollection&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Contain(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;NotContain(&lt;em&gt;expected&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;OnlyContain(x =&amp;gt; x.&lt;strong&gt;predicate&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are just a few examples. There are dozens more methods for these and many other types.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;And&lt;/em&gt;... assertions are chainable!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stateName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"OHIO"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;stateName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;StartWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"O"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"O"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HI"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exceptions
&lt;/h2&gt;

&lt;p&gt;I don't like the MS Test &lt;code&gt;[ExpectedException(type)]&lt;/code&gt; attribute. Among other shortcomings, it doesn't let you test the exception message.&lt;/p&gt;

&lt;p&gt;With Fluent Assertions, exception testing looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Throw&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello is not allowed at this moment"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...or with "arrange / act / assert" syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;action&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Throw&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello is not allowed at this moment"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extensibility
&lt;/h2&gt;

&lt;p&gt;Fluent Assertions is nicely set up to allow you to write your own "Should" extension methods.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://www.nuget.org/packages?q=fluent+assertions" rel="noopener noreferrer"&gt;many NuGet packages&lt;/a&gt; containing additional extension methods, including my own &lt;a href="https://www.nuget.org/packages/SparkyTestHelpers.Moq.Fluent/" rel="noopener noreferrer"&gt;SparkyTestHelpers.Moq.Fluent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  But wait, there's more!
&lt;/h2&gt;

&lt;p&gt;Fluent Assertions doesn't just give you better syntax for writing assertions - it gives you much better messages when an assertion fails, for example:&lt;/p&gt;

&lt;p&gt;With Assert.AreEqual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;correctNameSpelling&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bryan"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Brian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correctNameSpelling&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...the failure message is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Assert.AreEqual failed. Expected:&amp;lt;Brian&amp;gt;. Actual:&amp;lt;Bryan&amp;gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Fluent Assertions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;correctNameSpelling&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bryan"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;correctNameSpelling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Brian"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;..the failure message is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected correctNameSpelling to be "Brian", but "Bryan" differs near "yan" (index 2).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "differs near" and index are a bit of overkill for strings this short, but pretty handy for long strings!&lt;/p&gt;

&lt;p&gt;(BTW, this is not a trivial example. correctNameSpelling SHOULD BE "Brian" and if your parents named you "Bryan", you should call them and complain.😏)&lt;/p&gt;




&lt;p&gt;I like Fluent Assertions &lt;em&gt;a lot&lt;/em&gt;, and if you're writing .NET unit tests, I think you &lt;strong&gt;.&lt;em&gt;Should&lt;/em&gt;()&lt;/strong&gt; give it a try!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Custom .NET XML .config sections with SparkyTools.XmlConfig</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Wed, 07 Jul 2021 14:51:54 +0000</pubDate>
      <link>https://dev.to/sparky/custom-net-xml-config-sections-with-sparkytools-xmlconfig-30d6</link>
      <guid>https://dev.to/sparky/custom-net-xml-config-sections-with-sparkytools-xmlconfig-30d6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article is about dying technology - .NET framework&lt;/em&gt; &lt;strong&gt;web.config&lt;/strong&gt; and &lt;strong&gt;app.config&lt;/strong&gt; &lt;em&gt;XML files.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;.NET Core overhauled application configuration, jettisoning .config XML files in favor of app settings JSON files, and .NET 5 brings those configuration changes back into "Framework" .NET.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For those of us who are still acting as caretakers for geriatric .NET framework apps using .config files though, here's a tool to make it a bit easier...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to load configuration data for .NET application from a &lt;strong&gt;web.config&lt;/strong&gt; or &lt;strong&gt;app.config&lt;/strong&gt; section other than the built-in sections (e.g. appSettings, connectionStrings), you have to write a custom &lt;a href="https://msdn.microsoft.com/en-us/library/system.configuration.iconfigurationsectionhandler(v=vs.110).aspx"&gt;IConfigurationSectionHandler&lt;/a&gt; implementation to handle your custom .config section. It’s not hard to do, but it’s a fairly tedious coding exercise. What if you didn’t have to?...&lt;/p&gt;

&lt;h2&gt;
  
  
  SparkyTools.XmlConfig NuGet packages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SparkyTools.XmlConfig&lt;/strong&gt;: for .NET framework v4.6 and above

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/SparkyTools.XmlConfig/"&gt;NuGet package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BrianSchroer/SparkyTools/blob/master/SparkyTools.XmlConfig/api.md"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SparkyTools.XmlConfig.Fx&lt;/strong&gt;: for .NET framework v4-4.5.1

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/SparkyTools.XmlConfig.Fx/"&gt;NuGet package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BrianSchroer/SparkyTools/blob/master/SparkyTools.XmlConfig.Fx/api.md"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The code in these packages was inspired by a blog post called "&lt;a href="https://sites.google.com/site/craigandera/craigs-stuff/clr-workings/the-last-configuration-section-handler-i-ll-ever-need"&gt;The Last Configuration Section Handler I'll Ever Need&lt;/a&gt;" by Craig Andera that I read way back in 2003 🤯. I've been using this technique since then, and finally "NuGet-ed" it a few years ago.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ConfigurationSectionDeserializer / ConfigurationSectionListDeserializer
&lt;/h3&gt;

&lt;p&gt;These SparkyTools.XmlConfig classes makes it easy to load a strongly-typed object (or IList of objects) from a custom &lt;strong&gt;web.config&lt;/strong&gt; or &lt;strong&gt;app.config&lt;/strong&gt; file section without having to write a custom IConfigurationSectionHandler implementation.&lt;/p&gt;

&lt;p&gt;For the code examples below, I'll be using this incredibly realistic C# class definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Baz&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the .config file, register each custom section with a type of “SparkyTools.XmlConfig.&lt;strong&gt;ConfigurationSectionDeserializer&lt;/strong&gt;” or&lt;br&gt;
“SparkyTools.XmlConfig.&lt;strong&gt;ConfigurationSectionListDeserializer&lt;/strong&gt;”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Foo"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"SparkyTools.XmlConfig.ConfigurationSectionDeserializer, SparkyTools.XmlConfig"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"FooList"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"SparkyTools.XmlConfig.ConfigurationSectionListDeserializer, SparkyTools.XmlConfig"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(If you're using the SparkyTools.XmlConfig.&lt;em&gt;Fx&lt;/em&gt; package, the types/namespaces will be "...XmlConfig.&lt;em&gt;Fx&lt;/em&gt;.Config")&lt;/p&gt;

&lt;p&gt;In each registered custom section, specify the object type via the type attribute. Here's an single instance section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FooNamespace.Foo, FooAssemblyName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Bar&amp;gt;&lt;/span&gt;bar&lt;span class="nt"&gt;&amp;lt;/Bar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Baz&amp;gt;&lt;/span&gt;123.45&lt;span class="nt"&gt;&amp;lt;/Baz&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Foo&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and a “list” section: (Note that "type" is the "single" instance type, not "IList..."):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;FooList&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FooNamespace.Foo, FooAssemblyName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Foo&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Bar&amp;gt;&lt;/span&gt;bar1&lt;span class="nt"&gt;&amp;lt;/Bar&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Baz&amp;gt;&lt;/span&gt;111.11&lt;span class="nt"&gt;&amp;lt;/Baz&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Foo&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Foo&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Bar&amp;gt;&lt;/span&gt;bar2&lt;span class="nt"&gt;&amp;lt;/Bar&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Baz&amp;gt;&lt;/span&gt;222.22&lt;span class="nt"&gt;&amp;lt;/Baz&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Foo&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/FooList&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;strong&gt;XmlAttribute&lt;/strong&gt; attributes in your class definitions to tell the serializers to get properties from XML attributes rather than child XML elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;XmlAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;XmlAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Baz&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;FooList&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FooNamespace.Bar, FooAssemblyName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class="na"&gt;bar=&lt;/span&gt;&lt;span class="s"&gt;"bar1"&lt;/span&gt; &lt;span class="na"&gt;baz=&lt;/span&gt;&lt;span class="s"&gt;"111.11"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Foo&lt;/span&gt; &lt;span class="na"&gt;bar=&lt;/span&gt;&lt;span class="s"&gt;"bar2"&lt;/span&gt; &lt;span class="na"&gt;baz=&lt;/span&gt;&lt;span class="s"&gt;"222.22"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/FooList&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To read from your custom .config section, just call the ConfigurationSectionDeserializer or ConfigurationSectionListDeserializer &lt;strong&gt;Load&lt;/strong&gt; method, specifying the object type and the .config section name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="n"&gt;ConfigurationSectionDeserializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Foo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fooList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="n"&gt;ConfigurationSectionListDeserializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"FooList"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Dependency Injection
&lt;/h3&gt;

&lt;p&gt;My &lt;a href="https://www.nuget.org/packages/SparkyTools.DependencyProvider"&gt;SparkyTools.DependencyProvider&lt;/a&gt; NuGet package can be used to create &lt;strong&gt;DependencyProvider&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; instances for constructing classes with dependencies that aren't easily mockable. DependencyProvider&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; just has one method, "GetValue()", which returns a &lt;em&gt;T&lt;/em&gt; instance.&lt;/p&gt;

&lt;p&gt;Here's a class with a dependency of type "Foo" injected via a DependencyProvider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SparkyTools.DependencyProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Qux&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="n"&gt;_foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Qux&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDependencyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fooProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_foo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fooProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The static &lt;strong&gt;ConfigurationSectionDeserializer.DependencyProvider&lt;/strong&gt; and &lt;strong&gt;ConfigurationSectionListDeserializer.DependencyProvider&lt;/strong&gt;&lt;br&gt;
methods create DependencyProviders for loading data from custom .config file sections.&lt;/p&gt;

&lt;p&gt;Here's code for creating an instance of the Qux class shown above, injecting the Foo dependency from a custom .config file section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SparkyTools.XmlConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;qux&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Qux&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;ConfigurationSectionDeserializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DependencyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Foo"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  AppSettings.DependencyProvider
&lt;/h4&gt;

&lt;p&gt;A bit off this post's topic of custom .config file sections, but still in the general XML configuration topic - The static &lt;strong&gt;AppSettings.DependencyProvider()&lt;/strong&gt; method can be used to make classes that get values via &lt;strong&gt;ConfigurationManager.AppSettings&lt;/strong&gt; more unit-testable... &lt;/p&gt;

&lt;p&gt;Consider this class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Quux&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoThing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"featureEnabled"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Do that thang!&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to unit test it, you'll have to either have an app.config file with an appSettings section in your test project or have your test code do a static method call to set the value, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"serviceUrl"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://fakeurl"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and doing static things in unit tests is always a bad idea.&lt;/p&gt;

&lt;p&gt;Instead of using ConfigurationManager.AppSettings directly you can abstract your class to say "I need a function that returns configuration values for given keys":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Quux&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_getAppSetting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Quux&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDependencyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;appSettingsProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_getAppSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appSettingsProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoThing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_getAppSetting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureEnabled"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Do that thang!&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SparkyTools.XmlConfig.&lt;strong&gt;AppSettings.DependencyProvider()&lt;/strong&gt; creates a DependencyProvider&amp;lt;&lt;em&gt;Func&lt;/em&gt;&amp;lt;&lt;em&gt;string, string&lt;/em&gt;&amp;gt;&amp;gt;) that "wraps" &lt;strong&gt;ConfigurationManager.AppSettings&lt;/strong&gt;, so you can use it in your "real code":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SparkyTools.XmlConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;qux&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Quux&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DependencyProvider&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and "mock" the appSettingProvider dependency in your unit tests.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to test C# private methods / SparkyTestHelpers.NonPublic</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Sun, 21 Mar 2021 13:55:51 +0000</pubDate>
      <link>https://dev.to/sparky/how-to-test-c-private-methods-sparkytesthelpers-nonpublic-27ab</link>
      <guid>https://dev.to/sparky/how-to-test-c-private-methods-sparkytesthelpers-nonpublic-27ab</guid>
      <description>&lt;p&gt;How do you unit test private (or internal or protected) methods, properties and fields?&lt;/p&gt;

&lt;p&gt;The short answer, which you'll find if you do a web search for this question, is &lt;strong&gt;DON'T&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvurrxw46j5cbvsmghuax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvurrxw46j5cbvsmghuax.png" alt="Google search results say don't test private methods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can usually indirectly test the behavior of non-public members by testing the public members that use them. If you find yourself wanting to access non-public members from unit tests, it's a "code smell" that the class you're testing should be refactored to be testable.&lt;/p&gt;

&lt;p&gt;...Sometimes, though - maybe just temporarily so you can get tests wrapped around "legacy code" that you'll eventually be refactoring out, accessing non-public members from unit tests is a pragmatic choice. Here's how to do it:&lt;/p&gt;

&lt;p&gt;If you're using the "MSTest" (Microsoft.VisualStudio.TestTools.UnitTesting) framework, it has a &lt;strong&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.privatetype" rel="noopener noreferrer"&gt;PrivateObject&lt;/a&gt;&lt;/strong&gt; helper that can be used to access non-public members:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;PrivateObject&lt;/span&gt; &lt;span class="n"&gt;privateObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PrivateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;privateObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateMethodName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a different test framework, or if you want to use easier syntax (to do this thing that I'm telling you not to do 🙂), you can the ".NonPublic()" extension method fluent syntax included in the &lt;strong&gt;SparkyTestHelpers.NonPublic&lt;/strong&gt; NuGet package:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/SparkyTestHelpers.NonPublic/" rel="noopener noreferrer"&gt;NuGet package&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/tree/master/SparkyTestHelpers.NonPublic" rel="noopener noreferrer"&gt;Source code&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/blob/master/SparkyTestHelpers.NonPublic/api.md" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// methods&lt;/span&gt;
&lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateMethod"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateMethodWithArgs"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;value1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateFunction"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;typedValue1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateFunction"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateFunctionWithArgs"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;typedValue2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateFunctionWIthArgs"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// properties&lt;/span&gt;
&lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateDateProperty"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateDateProperty"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;typedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateDateProperty"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// fields:&lt;/span&gt;
&lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_stringField"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_stringField"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;typedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_stringField"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples above are for "instance" members. For accessing static members, use &lt;strong&gt;.StaticMethod&lt;/strong&gt;, &lt;strong&gt;.StaticProperty&lt;/strong&gt; and &lt;strong&gt;.StaticField&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;StaticMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PrivateStaticFunction"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;boolValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;StaticProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StaticProperty"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stringValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subjectUnderTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonPublic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;StaticField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_staticField"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MsTest's &lt;strong&gt;PrivateObject&lt;/strong&gt; has a lot of methods with binding flag, type array, CultureInfo, etc. arguments. I honestly don't know when those would be needed, but if you're unable to accomplish what you need to with the fluent syntax described above, you can use this package's SparkyTestHelpers.NonPublic.&lt;strong&gt;NonPublicMembers&lt;/strong&gt; class. It's a "clone" of &lt;strong&gt;PrivateObject&lt;/strong&gt;, and uses the same syntax.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>testing</category>
      <category>unittesting</category>
    </item>
    <item>
      <title>SparkyTestHelpers: AspNetMvc</title>
      <dc:creator>Brian Schroer</dc:creator>
      <pubDate>Fri, 28 Feb 2020 23:09:09 +0000</pubDate>
      <link>https://dev.to/sparky/sparkytesthelpers-aspnetmvc-1pc8</link>
      <guid>https://dev.to/sparky/sparkytesthelpers-aspnetmvc-1pc8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;.NET Framework version:&lt;br&gt;
&lt;a href="https://www.nuget.org/packages/SparkyTestHelpers.AspNetMvc"&gt;NuGet package&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/tree/master/SparkyTestHelpers.AspNetMvc"&gt;Source code&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/blob/master/SparkyTestHelpers.AspNetMvc/api.md"&gt;API documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;.NET Core version:&lt;br&gt;
&lt;a href="https://www.nuget.org/packages/SparkyTestHelpers.AspNetMvc.Core"&gt;NuGet package&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/tree/master/SparkyTestHelpers.AspNetMvc.Core"&gt;Source code&lt;/a&gt; | &lt;a href="https://github.com/BrianSchroer/sparky-test-helpers/blob/master/SparkyTestHelpers.AspNetMvc.Core/api.md"&gt;API documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These NuGet packages (one for the “classic” ASP.NET MVC Framework and one for ASP.NET Core MVC) contain helpers for testing that MVC controller actions return the expected results:&lt;/p&gt;




&lt;h2&gt;
  
  
  Controller Testers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ControllerTester&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; tests action methods of controllers that inherit from System.Web.MVC.&lt;strong&gt;Controller&lt;/strong&gt; / Microsoft.AspNetCore.Mvc.&lt;strong&gt;ControllerBase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ApiControllerTester&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; tests action methods of controllers that inherit from the .NET Framework System.Web.Http.&lt;strong&gt;ApiController&lt;/strong&gt; class.&lt;/p&gt;

&lt;p&gt;The general syntax is:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tester&lt;/em&gt;&lt;br&gt;
    .&lt;strong&gt;Action&lt;/strong&gt;(&lt;em&gt;action selection expression&lt;/em&gt;)&lt;br&gt;
    .&lt;strong&gt;When&lt;/strong&gt;(&lt;em&gt;optional code to "arrange" test conditions&lt;/em&gt;)&lt;br&gt;
    .&lt;strong&gt;Expecting&lt;/strong&gt;...(&lt;em&gt;optional code to set up assert expectations&lt;/em&gt;)&lt;br&gt;
    .&lt;strong&gt;Test&lt;/strong&gt;...()&lt;/p&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//ControllerTester:&lt;/span&gt;

    &lt;span class="n"&gt;moviesControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;moviesControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingViewName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Details"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpectingModel&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Office Space"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;moviesControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testInvalidModel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestRedirectToAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Errors"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;moviesControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testValidModel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingViewName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UpdateSuccessful"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestRedirectToRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/UpdateSuccessful"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//ApiControllerTester:&lt;/span&gt;

   &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt; 
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAllMovies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

   &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt; 
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenRequestHasQueryStringParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;QueryStringParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestOkNegotiatedContentResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Office Space"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestBadRequestResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestOkResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Instantiation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ControllerTester&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; and &lt;strong&gt;ApiControllerTester&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt; can be created either via their constructors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;homeControllerTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ControllerTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;testHomeController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ApiControllerTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MoviesApiController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;testMoviesController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...or via &lt;strong&gt;CreateTester&lt;/strong&gt; extension methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;homeControllerTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testHomeController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTester&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;moviesApiControllerTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testMoviesController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTester&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  "Action" Methods
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;tester&lt;/em&gt;.&lt;strong&gt;Action&lt;/strong&gt; methods use lambda expressions to specify the controller action to be tested. The expressions enable Intellisense completion when you "dot on" to the controller argument.&lt;/p&gt;

&lt;p&gt;The syntax for parameterless actions is "&lt;em&gt;.Action(controller =&amp;gt; controller&lt;/em&gt;.&lt;strong&gt;actionName&lt;/strong&gt;)", e.g. &lt;code&gt;.Action(x =&amp;gt; x.Index)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For actions with parameters, the syntax is "&lt;em&gt;.Action(controller =&amp;gt; () =&amp;gt; controller&lt;/em&gt;.&lt;strong&gt;actionName&lt;/strong&gt;(&lt;em&gt;arguments&lt;/em&gt;))", e.g. &lt;code&gt;.Action(x =&amp;gt; () =&amp;gt; x.Get(3))&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  "When" methods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WhenRequestHasQueryStringParameters&lt;/strong&gt;(&lt;em&gt;string siteUrlPrefix, NameValueCollection queryStringParameters&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhenRequestHasQueryStringParameters&lt;/strong&gt;(&lt;em&gt;NameValueCollection queryStringParameters&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhenRequestHasQueryStringParameters&lt;/strong&gt;(&lt;em&gt;string siteUrlPrefix, params QueryStringParameter[] queryStringParameters&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhenRequestHasQueryStringParameters&lt;/strong&gt;(&lt;em&gt;params QueryStringParameter[] queryStringParameters&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;WhenRequestHasQueryStringParameters&lt;/strong&gt; methods (&lt;strong&gt;ApiControllerTester&lt;/strong&gt; only) set up the controller's &lt;strong&gt;Request&lt;/strong&gt; property with a &lt;strong&gt;RequestUri&lt;/strong&gt; containing the specified parameters. If the &lt;strong&gt;siteUrlPrefix&lt;/strong&gt; isn't specified (it usually won't matter in unit tests), the value "&lt;a href="http://localhost"&gt;http://localhost&lt;/a&gt;" is used.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WhenModelStateIsValidEquals&lt;/strong&gt;(&lt;em&gt;bool isValid&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method sets up the controller's &lt;strong&gt;ModelState&lt;/strong&gt; for testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When&lt;/strong&gt;(&lt;em&gt;Action action&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method can be used to "arrange" any conditions necessary for the test, e.g. setting up a mocked interface method.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Expecting" methods (&lt;strong&gt;ControllerTester&lt;/strong&gt; only):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ExpectingViewName&lt;/strong&gt;(string expectedViewName)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ExpectingViewName&lt;/strong&gt; sets up automatic validation when followed by &lt;strong&gt;TestView&lt;/strong&gt; or &lt;strong&gt;TestPartialView&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ExpectingModel&amp;lt;&lt;em&gt;TModel&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;*TModel&lt;/em&gt;&amp;gt; validate*) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ExpectingModel&lt;/strong&gt; sets up automatic model type and possibly content (the &lt;strong&gt;validate&lt;/strong&gt; callback action is optional) validation when followed by &lt;strong&gt;TestView&lt;/strong&gt; or &lt;strong&gt;TestJson&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Test" methods - ControllerTester:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ControllerTester&lt;/strong&gt;&amp;lt;&lt;em&gt;TController&lt;/em&gt;&amp;gt; has several .Test... methods used to assert that the controller action returns the expected ActionResult implementation (The method name suffixes correspond to ...Result types (e.g. &lt;strong&gt;TestView&lt;/strong&gt; tests &lt;strong&gt;ViewResult&lt;/strong&gt;). There are methods for all of the standard result types, plus the generic TestResult&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;validate&lt;/em&gt; "callback" actions, which can be used to validate the result contents, are optional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestContent&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;ContentResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestEmpty&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;EmptyResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestFile&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;FileResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestJson&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;JsonResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestPartialView&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;PartialViewResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirect&lt;/strong&gt;(&lt;em&gt;string expectedUrl, Action&lt;/em&gt;&amp;lt;&lt;em&gt;RedirectResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToAction&lt;/strong&gt;(&lt;em&gt;string expectedActionName, Action&lt;/em&gt;&amp;lt;&lt;em&gt;RedirectToRouteResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToRoute&lt;/strong&gt;(&lt;em&gt;string expectedRoute, Action&lt;/em&gt;&amp;lt;&lt;em&gt;RedirectToRouteResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestView&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;ViewResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestResult&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  "Test" methods - ApiControllerTester - for actions that return an IHttpActionResult:
&lt;/h3&gt;

&lt;p&gt;There several &lt;strong&gt;.Test&lt;/strong&gt;... methods used to assert that API controller actions return the expected &lt;strong&gt;IHttpActionResult&lt;/strong&gt; implementation. There are methods for all of the standard result types, plus the generic &lt;strong&gt;TestResult&amp;lt;&lt;em&gt;THttpActionResultType&lt;/em&gt;&amp;gt;&lt;/strong&gt; method:&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;validate&lt;/em&gt; "callback" actions, which can be used to validate the result contents, are optional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestBadRequestErrorMessageResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;BadRequestErrorMessageResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestBadRequestResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;BadRequestResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestConflictResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;ConflictResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestCreatedAtRouteNegotiatedContentResult&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;CreatedAtRouteNegotiatedContentResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestCreatedNegotiatedContentResult&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;CreatedNegotiatedContentResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestExceptionResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;ExceptionResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestFormattedContentResult&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;(&lt;em&gt;Action&amp;lt;&lt;/em&gt;&lt;em&gt;FormattedContentResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestInternalServerErrorResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;InternalServerErrorResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestInvalidModelStateResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;InvalidModelStateResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestJsonResult&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;JsonResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestNegotiatedContentResult&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;NegotiatedContentResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestNotFoundResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;NotFoundResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestOkNegotiatedContentResult&lt;/strong&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;OkNegotiatedContentResult&lt;/em&gt;&amp;lt;&lt;em&gt;T&lt;/em&gt;&amp;gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestOkResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;OkResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;RedirectResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToRouteResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;RedirectToRouteResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestResponseMessageResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;ResponseMessageResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestStatusCodeResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;StatusCodeResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestUnauthorizedResult&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;UnauthorizedResult&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestResult&amp;lt;&lt;em&gt;THttpActionResultType&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;THttpActionResultType&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  "Test" methods - ApiControllerTester - for actions that return an HttpResponseMessage:
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;validate&lt;/em&gt; "callback" actions, which can be used to validate the result contents, are optional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;HttpResponseMessage&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...calls controller action, validates &lt;em&gt;HttpResponseMessage.StatusCode&lt;/em&gt; (if &lt;strong&gt;ExpectingHttpStatusCode(&lt;em&gt;HttpStatusCode&lt;/em&gt;)&lt;/strong&gt; has been called) and returns the &lt;strong&gt;HttpResponseMessage&lt;/strong&gt; returned from the action.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestContentString&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;string&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...calls controller action, validates &lt;em&gt;HttpResponseMessage.StatusCode&lt;/em&gt; (if &lt;strong&gt;ExpectingHttpStatusCode(&lt;em&gt;HttpStatusCode&lt;/em&gt;)&lt;/strong&gt; has been called) and returns the &lt;strong&gt;HttpResponseMessage.Content&lt;/strong&gt; string.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestContentJsonDeserialization&amp;lt;&lt;em&gt;TContent&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&lt;/em&gt;&amp;lt;&lt;em&gt;TContent&lt;/em&gt;&amp;gt; &lt;em&gt;validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...calls controller action, validates &lt;em&gt;HttpResponseMessage.StatusCode&lt;/em&gt; (if &lt;strong&gt;ExpectingHttpStatusCode(&lt;em&gt;HttpStatusCode&lt;/em&gt;)&lt;/strong&gt; has been called) and returns the &lt;strong&gt;HttpResponseMessage.Content&lt;/strong&gt;'s JSON string deserialized to a &lt;em&gt;TContent&lt;/em&gt; instance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing ASP.NET MVC Core RazorPages
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PageModelTester&amp;lt;&lt;em&gt;TPageModel&lt;/em&gt;&amp;gt;
&lt;/h3&gt;

&lt;p&gt;ASP.NET MVC Razor Page &lt;strong&gt;PageModel&lt;/strong&gt;s have a lot in common with Controllers (they kind of combine the “C” and “M” of MVC), so the Razor &lt;strong&gt;PageModelTester&lt;/strong&gt; has similar &lt;strong&gt;Action&lt;/strong&gt;, &lt;strong&gt;When&lt;/strong&gt;..., &lt;strong&gt;ExpectingModel&lt;/strong&gt; and *&lt;em&gt;Test&lt;/em&gt;... methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;… methods assert that the PageModel action returns the expected &lt;strong&gt;IActionResult&lt;/strong&gt; implementation. There are methods for many standard result types, plus the generic &lt;strong&gt;TestResult&lt;/strong&gt;&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt; method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestContent&lt;/strong&gt;(&lt;em&gt;(optional) Action&amp;lt;&lt;em&gt;ContentResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestFile&lt;/strong&gt;(&lt;em&gt;(optional) Action&amp;lt;&lt;em&gt;FileResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestJsonResult&lt;/strong&gt;(&lt;em&gt;(optional) Action&amp;lt;&lt;em&gt;JsonResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestPage&lt;/strong&gt;(&lt;em&gt;(optional) Action&amp;lt;&lt;em&gt;PageResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToAction&lt;/strong&gt;(&lt;em&gt;string expecteActionName, string expectedControllerName, object expectedRouteValues, (optional) Action validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToPage&lt;/strong&gt;(&lt;em&gt;string expectedPageName, (optional) Action&amp;lt;&lt;em&gt;RedirectToPageResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestRedirectToRoute&lt;/strong&gt;(&lt;em&gt;string expectedRoute, (optional) Action&amp;lt;&lt;em&gt;RedirectToRouteResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestResult&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;(optional) Action&amp;lt;&lt;em&gt;TActionResultType&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;validate&lt;/em&gt; “callback” methods may be used for additional data assertions, beyond testing proper return types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;homeModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HomeModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* with test dependencies */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PageModelTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;homeModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;pageTester&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnGet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;pageTester&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedErrorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorMessage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;pageTester&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestRedirectToPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UpdateSuccessful"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing ASP.NET MVC Core ViewComponents
&lt;/h2&gt;

&lt;p&gt;The .NET Core framework for ASP.NET MVC has another nice new tool that "framework" MVC doesn't: &lt;strong&gt;ViewComponents&lt;/strong&gt;, a powerful alternative to partial views.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;ViewComponent&lt;/strong&gt; test helper classes have a lot of similarities with their &lt;strong&gt;Controller&lt;/strong&gt; and Razor &lt;strong&gt;PageModel&lt;/strong&gt; counterparts:&lt;/p&gt;

&lt;h2&gt;
  
  
  ViewComponentTester methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Invocation methods
&lt;/h3&gt;

&lt;p&gt;Similar to the ControllerTester and PageModelTester "Action" methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invocation&lt;/strong&gt;(&lt;em&gt;synchronous Invoke method expression&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invocation&lt;/strong&gt;(&lt;em&gt;async InvokeAsync method expression&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  "When" methods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WhenModelStateIsValidEquals&lt;/strong&gt;(&lt;em&gt;bool isValid&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method sets up the ViewComponent's &lt;strong&gt;ModelState&lt;/strong&gt; for testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When&lt;/strong&gt;(&lt;em&gt;Action action&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method can be used to "arrange" any conditions necessary for the test, e.g. setting up a mocked interface method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expecting methods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;.&lt;strong&gt;ExpectingViewName&lt;/strong&gt;(&lt;em&gt;string expectedViewName&lt;/em&gt;) — used with .TestView&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ExpectingModel&amp;lt;&lt;em&gt;TModelType&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;&lt;em&gt;TModelType&lt;/em&gt;&amp;gt; validate&lt;/em&gt;) — used with .&lt;strong&gt;TestView&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test methods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TestContent&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;&lt;em&gt;ContentViewComponentResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestHtmlContent&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;&lt;em&gt;HtmlContentViewComponentResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestView&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;&lt;em&gt;ViewViewComponentResult&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestResult&amp;lt;&lt;em&gt;TViewComponentResultType&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Action&amp;lt;&lt;em&gt;TViewComponentResultType&lt;/em&gt;&amp;gt; validate&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;WhenModelStateIsValidEquals&lt;/strong&gt;(&lt;em&gt;bool isValid&lt;/em&gt;) — used to test conditional logic based on ModelState.IsValid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All &lt;em&gt;validate&lt;/em&gt; “callback” actions shown above are optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SparkyTestHelpers.AspNetMvc&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ViewComponentTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FooViewComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingViewName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Baz&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ViewComponentTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FooViewComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenModelStateIsValidEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExpectingViewName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Errors"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ViewComponentTester&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BarViewComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvokeAsnyc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TestView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing Routing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;These test helpers are only in the .NET Framework NuGet package. Routing has been rewritten for .NET Core, and I’ll have to figure out how to mock a bunch of different dependencies before I can add routing test helpers for .NET Core. It looks like Ivaylo Kenov has figured it out, so if you want to unit test ASP.NET Core routing now, I suggest you look at &lt;a href="https://github.com/ivaylokenov/MyTested.AspNetCore.Mvc"&gt;his GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  RouteTester
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RouteTester&lt;/strong&gt; and &lt;strong&gt;RoutingAsserter&lt;/strong&gt; provide methods to assert that a given relative URL maps to the expected &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.web.routing.routedata.values?view=netframework-4.7#System_Web_Routing_RouteData_Values"&gt;RouteData.Values&lt;/a&gt;. The &lt;strong&gt;RoutingAsserter.AssertMapTo&lt;/strong&gt; overloads provide multiple ways to specify the expected values…&lt;/p&gt;

&lt;h4&gt;
  
  
  RouteTester Constructors
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;public &lt;strong&gt;RouteTester&lt;/strong&gt;((Action&amp;lt;&lt;em&gt;RouteCollection&lt;/em&gt;&amp;gt; routeRegistrationMethod*)&lt;/li&gt;
&lt;li&gt;public &lt;strong&gt;RouteTester&lt;/strong&gt;(&lt;em&gt;AreaRegistration areaRegistration&lt;/em&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SparkyTestHelpers.AspNetMvc.Routing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;routeTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RouteTester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RouteConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterRoutes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;areaRouteTester&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RouteTester&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FooAreaRegistration&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  RouteTester Methods
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;.&lt;strong&gt;ForUrl&lt;/strong&gt;(&lt;em&gt;string relativeUrl&lt;/em&gt;) — creates a new ##RoutingAsserter## instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RoutingAsserter
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Nethods
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AssertMapTo&lt;/strong&gt;(&lt;em&gt;IDictionary&amp;lt;&lt;em&gt;string, object&lt;/em&gt;&amp;gt; expectedValues&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AssertMapTo&lt;/strong&gt;(&lt;em&gt;object routeValues&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AssertMapTo&lt;/strong&gt;(&lt;em&gt;string controller, string action, (object id)&lt;/em&gt;) — id defaults to null&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AssertMapTo&amp;lt;&lt;em&gt;TController&lt;/em&gt;&amp;gt;&lt;/strong&gt;(&lt;em&gt;Expression&amp;lt;&lt;em&gt;Func&lt;/em&gt;&amp;lt;&lt;em&gt;TController, Func&lt;/em&gt;&amp;lt;&lt;em&gt;ActionResult&lt;/em&gt;&amp;gt;&lt;em&gt;&amp;gt;&lt;/em&gt;&amp;gt; actionExpression&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AssertRedirectTo&lt;/strong&gt;(&lt;em&gt;string expectedUrl, (HttpStatusCode expectedStatusCode)&lt;/em&gt;) — expectedStatusCode defaults to HttpStatusCode.Redirect (302)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Examples
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default.aspx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertRedirectTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/LegacyRedirect"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// alternate syntaxes for asserting Home/Index routing:&lt;/span&gt;

&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="s"&gt;"controller"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Index"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home/Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// alternate syntaxes for asserting Order/Details/3 routing:&lt;/span&gt;

&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order/Details/3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"controller"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Details"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order/Details/3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order/Details/3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;routeTester&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order/Details/3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AssertMapTo&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Happy Testing!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>testing</category>
      <category>unittesting</category>
    </item>
  </channel>
</rss>
