<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Todd Schiller - testing</title><link href="https://toddschiller.com/" rel="alternate"></link><link href="https://toddschiller.com/feeds/tag/testing.atom.xml" rel="self"></link><id>https://toddschiller.com/</id><updated>2013-12-29T00:00:00-05:00</updated><subtitle>Human ✘ Artificial Intelligence</subtitle><entry><title>Improving unit testing with data provenance</title><link href="https://toddschiller.com/blog/improving-unit-testing-with-data-provenance.html" rel="alternate"></link><published>2013-12-29T00:00:00-05:00</published><updated>2013-12-29T00:00:00-05:00</updated><author><name>Todd Schiller</name></author><id>tag:toddschiller.com,2013-12-29:/blog/improving-unit-testing-with-data-provenance.html</id><summary type="html">&lt;p&gt;In an ideal world, each unit test would run in isolation. In practice,
environment setup (e.g., database schema setup) is expensive, so it's
common to share resources between tests.&lt;/p&gt;
&lt;p&gt;In the worst case, interference between tests can actually mask bugs
that the test suite would otherwise catch &lt;a href="#test-dependence"&gt;[1]&lt;/a&gt;. In …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In an ideal world, each unit test would run in isolation. In practice,
environment setup (e.g., database schema setup) is expensive, so it's
common to share resources between tests.&lt;/p&gt;
&lt;p&gt;In the worst case, interference between tests can actually mask bugs
that the test suite would otherwise catch &lt;a href="#test-dependence"&gt;[1]&lt;/a&gt;. In my
experience, a
more common outcome has been that sharing resources hinders debugging
by making it difficult to determine where data came from (its provenance).&lt;/p&gt;
&lt;p&gt;An effective way to address this issue is to &amp;quot;tag&amp;quot; data, at the time of its
creation,
with information about the test context. For example, in
one of my projects, we use the test name to identify data provenance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetTestContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testMethods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFrames&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testAttr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestMethodAttribute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMethod&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;GetCustomAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testAttr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;attrs&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="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Debug&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="n"&gt;testMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unique test context required&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testMethods&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;GetMethod&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&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;Note that &lt;code&gt;Debug.Assert&lt;/code&gt; (or &lt;code&gt;Trace.Assert&lt;/code&gt; for release builds) is
used instead of throwing an exception. Unlike an exception, an
assertion failure cannot be caught by the program under test or the
test suite. As a rule, instrumentation code should &lt;em&gt;never&lt;/em&gt; throw an
exception. Instead, instrumentation code should crash if an assumption
is violated.&lt;/p&gt;
&lt;p&gt;In our case, storing the test name with data records did not require
any modifications to the data model — the records of interest
already had an associated &lt;code&gt;string&lt;/code&gt; property that does not affect
program behavior (used to store user-entered notes). However,
attaching &lt;code&gt;string&lt;/code&gt; metadata is not an option in all cases. In these
cases, you may be able to locate a non-functional part of the data
record (e.g., a unique identifier field) to store partial provenance
information, maintaining a mapping to the test context as necessary.&lt;/p&gt;
&lt;p&gt;The benefit of storing the information in the data record itself as
opposed to doing a more complex form of tracking, e.g. using a CLR
program instrumentation tool, is that the provenance data is not lost
when the record is persisted in an outside resource such as a
database.&lt;/p&gt;
&lt;p&gt;This leaves the question of how to invoke provenance tagging
methods. If the test suite itself is setting data, it's easiest just
to call the method directly. If not, the standard approach works of
abstracting the data creation method as an interface and then using a
&lt;a href="http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs"&gt;fake (i.e. mock)&lt;/a&gt;
when testing. An interesting alternative (which I
haven't yet tried) is to use the
&lt;a href="http://msdn.microsoft.com/en-us/library/ff798308.aspx"&gt;Microsoft's Moles tool&lt;/a&gt;
to &amp;quot;detour&amp;quot; the property setters to insert the test context.
Because Moles works via the CLR's just-in-time compiler, this could be done
without modifying the program under test.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;span id="test-dependence"&gt;[1]&lt;/span&gt;
&lt;a href="http://homes.cs.washington.edu/~szhang/pdf/UW-CSE-13-07-02.PDF"&gt;Test Dependence: Theory and Manifestation&lt;/a&gt;
. Jochen Wuttke et al. University of Washington Technical Report
UW-CSE-13-07-02. 2013.&lt;/p&gt;
</content><category term="Testing"></category><category term="testing"></category><category term="data provenance"></category><category term="C#"></category></entry></feed>