<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Matt Manela's Blog : testing</title><link>http://blogs.msdn.com/matt/archive/tags/testing/default.aspx</link><description>Tags: testing</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>DRY and Unit Tests don’t mix well</title><link>http://blogs.msdn.com/matt/archive/2009/07/12/dry-and-unit-tests-don-t-mix-well.aspx</link><pubDate>Mon, 13 Jul 2009 06:08:50 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9830902</guid><dc:creator>MattManela</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/matt/comments/9830902.aspx</comments><wfw:commentRss>http://blogs.msdn.com/matt/commentrss.aspx?PostID=9830902</wfw:commentRss><description>&lt;p&gt;When reading&lt;em&gt; &lt;/em&gt;source code, I sometimes come across unappealing code(sometimes even my own).&amp;#160; However, there is one kind of “bad code” I see quite frequently.&amp;#160; It is a set of unit tests which have had the &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" target="_blank"&gt;DRY (Don't Repeat Yourself)&lt;/a&gt; principle unduly forced upon them.&amp;#160; DRY is the idea that you shouldn’t have to write the same code over and over; abstract it in a function or a class and just call the abstraction.&amp;#160; This is all well and good in most cases, but I find it misguided when applied to a test case.&amp;#160; &lt;/p&gt;  &lt;p&gt;A test case should be like a simple short story.&amp;#160; The characters are introduced, action/conflict occurs and then resolution takes place (sometimes with a moral).&amp;#160; This (kind of) corresponds to the 3 steps of a unit test: arrange, act and assert.&amp;#160; You arrange and setup what you need for your test to run, you perform the action that you are trying to test and then you assert the results. The issue I find is that a coder, in attempting to apply DRY to his test cases, will abstract away all of the arrange step into a function often with a name like SetupExpectations or just Setup.&amp;#160; This goes against the point of a test case. A test case needs to be concise and tell me everything I need to know about how that one bit of functionality works. I don’t want to jump around the test class trying to read one test case. To me, this is like reading a book that says, “If you want to learn about the characters in this book please open this other book.”&amp;#160; This doesn’t stop you from understanding the test, but it slows you down…and is just annoying.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;That is why I will come out and say &lt;strong&gt;do not apply DRY haphazardly to test cases&lt;/strong&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9830902" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/matt/archive/tags/Programming/default.aspx">Programming</category><category domain="http://blogs.msdn.com/matt/archive/tags/testing/default.aspx">testing</category></item><item><title>Code Assumptions</title><link>http://blogs.msdn.com/matt/archive/2009/02/01/code-assumptions.aspx</link><pubDate>Mon, 02 Feb 2009 07:13:42 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9389368</guid><dc:creator>MattManela</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/matt/comments/9389368.aspx</comments><wfw:commentRss>http://blogs.msdn.com/matt/commentrss.aspx?PostID=9389368</wfw:commentRss><description>&lt;p&gt;My co-workers and I recently came across a piece of code which exposed some assumptions we had about the “correct” behavior of two functions; these assumptions turned out to be false.&amp;#160; The code dealt with determining if the IP of a&amp;#160; request coming into to a website matches a certain range of IP address. The range of IP address was defined in a config file and could look something like this:&lt;/p&gt;  &lt;p&gt;156.27.1.2, 156.*.*.*, 127.0.0.* &lt;/p&gt;  &lt;p&gt;Where the * acts as a wild card and each comma-separated IP format is matched against the incoming IP address. The code’s intended behavior is that specifying no IP address formats in the config would mean that no IP addresses would match the format.&amp;#160; We noticed that this wasn’t holding true.&amp;#160; We went to examine the code that was performing the matching (which none of us had written and sadly has NO test cases around it).&amp;#160; The code boiled down to something like this:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; DoesIPMatchIPFormat(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; incomingIpAddress, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; ipFormatsFromConfig)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt;(var ipFormat &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; ipFormatsFromConfig.Split(&lt;span style="color: #006080"&gt;','&lt;/span&gt;))&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         Regex re = ConvertFormatIntoRegex(ipFormat);&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt;(rs.Match(incomingIpAddress).Success) &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Where ipFormatsFromConfig is the ip range of formats shown above separated by “,” or an empty string if no formats are given. On first glance we all were confused by why this didn’t work and then as we looked into it we realized we had made some incorrect assumptions.&lt;/p&gt;

&lt;p&gt;First, we assumed that “”.Split(‘,’) would return a zero element array.&amp;#160; This assumption is false. It returns a 1 element array containing the empty string.&amp;#160; &lt;/p&gt;

&lt;p&gt;After we learned this we encountered our second assumption.&amp;#160; We assumed that a regular expression whose pattern was an empty string would not match anything. (ConvertFormatIntoRegex returned just that).&amp;#160; This assumption is also false.&amp;#160; After learning the true behavior, it makes sense why it behaves this way.&amp;#160; (A DFA with only one state must accept every input. ) &lt;/p&gt;

&lt;p&gt;Our assumptions about the “correct” behavior of functions reflect similar mistakes made by all programmers; assuming that you don’t make these kinds of assumptions is a very dangerous way to program and reinforces the importance of unit testing.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9389368" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/matt/archive/tags/Programming/default.aspx">Programming</category><category domain="http://blogs.msdn.com/matt/archive/tags/testing/default.aspx">testing</category></item></channel></rss>