<?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: Andrew R</title>
    <description>The latest articles on DEV Community by Andrew R (@steadylatch).</description>
    <link>https://dev.to/steadylatch</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4173940%2F653a9a1d-096d-4270-b10f-6a856bef446a.png</url>
      <title>DEV Community: Andrew R</title>
      <link>https://dev.to/steadylatch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/steadylatch"/>
    <language>en</language>
    <item>
      <title>Regression-testing an Excel/VBA report with golden files</title>
      <dc:creator>Andrew R</dc:creator>
      <pubDate>Mon, 12 Oct 2026 23:04:34 +0000</pubDate>
      <link>https://dev.to/steadylatch/regression-testing-an-excelvba-report-with-golden-files-m39</link>
      <guid>https://dev.to/steadylatch/regression-testing-an-excelvba-report-with-golden-files-m39</guid>
      <description>&lt;p&gt;The VBA that a business depends on is rarely a library. Usually it's one macro that reads a few files, builds a report and saves it. You can unit-test that (Rubberduck has a unit-testing framework for VBA), but the client's question is simpler: &lt;em&gt;is the report the same as yesterday, apart from what I asked you to change?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A golden file answers exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run the macro once on known input and check the output by hand. Once it's right, &lt;strong&gt;approve&lt;/strong&gt; it and keep it as the golden file.&lt;/li&gt;
&lt;li&gt;After each change, run the macro on the same input and &lt;strong&gt;compare&lt;/strong&gt; the new output with the golden, cell by cell.&lt;/li&gt;
&lt;li&gt;Every difference is a bug or a change you meant to make. If you meant it, approve the new output on purpose. Don't let anything overwrite the golden quietly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Making the comparison worth reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match rows by key as well as by position.&lt;/strong&gt; Sort the report differently and a positional comparison flags every row. Match on a key column (Store, OrderId) and you only see the rows that changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set tolerances per column.&lt;/strong&gt; Currency rounding shouldn't fail the check. An order count should match exactly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report where and by how much.&lt;/strong&gt; "Different" tells you nothing. "Row with Store=Central, column Revenue (D4): expected 92.71, found 60.71, delta -32" tells you where to look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empty is not zero.&lt;/strong&gt; An empty required cell in the input is its own failure. Don't read it as 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unreadable output never passes.&lt;/strong&gt; If the file won't open, that's a failure. It doesn't mean "no differences".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A real example
&lt;/h2&gt;

&lt;p&gt;In the demo I recorded, the bug is one character in a date filter: &lt;code&gt;&amp;lt;=&lt;/code&gt; became &lt;code&gt;&amp;lt;&lt;/code&gt;, so the report drops the last day of the month. The macro runs without an error. The comparison against the golden reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compare monthly-report-xlsx: Mismatch: 8 differences (1 within tolerance)
  - row with Store=Central, column 'Orders' (cell B4): expected '4', found '3' (delta -1; no tolerance: exact match required).
  - row with Store=Central, column 'Revenue' (cell D4): expected '92.71', found '60.71' (delta -32, outside the tolerance of ±0.005).
  - row with Store=Market, column 'Units' (cell C5): expected '13', found '11' (delta -2; no tolerance: exact match required).
 EXIT 1  fail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the exit code too. 1 means "a check failed", so a script or a CI job can stop right there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Excel is the hard part
&lt;/h2&gt;

&lt;p&gt;Microsoft doesn't support automating Office with nobody at the machine, so hosted CI (GitHub Actions and similar) is the wrong place to run the macro. What I do instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run the macro locally, on a &lt;strong&gt;copy&lt;/strong&gt; of the workbook, with a time limit, and keep its output only if every step worked;&lt;/li&gt;
&lt;li&gt;commit that output and let CI compare it with the golden. CI never needs Office.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CI will catch an output that differs. It can't prove the committed output came from the committed workbook, so that part is still on you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the example comes from
&lt;/h2&gt;

&lt;p&gt;The example above comes from a public demo repo, with the four scenarios and the full text of a run: &lt;a href="https://github.com/KBroneZ/excel-vba-regression-testing-demo" rel="noopener noreferrer"&gt;https://github.com/KBroneZ/excel-vba-regression-testing-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The short version: before you touch a macro again, approve a golden output, and compare against it afterwards. Any tooling works, a careful diff by hand included.&lt;/p&gt;

&lt;p&gt;There's a longer step-by-step version with two PowerShell scripts you can run on your own workbook (one exports the values to CSV, the other compares them by key with tolerances): &lt;a href="https://steadylatch.com/guides/excel-vba-regression-testing" rel="noopener noreferrer"&gt;https://steadylatch.com/guides/excel-vba-regression-testing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assisted: drafted with an AI model and checked line by line by me against the recorded demo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>excel</category>
      <category>vba</category>
      <category>powershell</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
