[TFUI] Re: unit testing advice

Phlip <[email protected]> Fri, 01 Aug 2008 18:22:31 -0700
Newsgroups gmane.comp.programming.test-first-user-interfaces
Message-ID <[email protected]>
--PgMNv9sLzVj6sTwkBy-gCOqKoHn5pG6nUuRgNzL
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Shadowfirebird wrote:

> I've found this lovely bit of example code for mocha, though -- see
> below.  If I understand you correctly, you seem to be saying that you
> *don't* test the output routine; you use a mock to fake it so that you
> can test the rest of the code?

Consider a test T that calls A, which calls B.

Sometimes, a B is cheap to assemble, so T can assemble B, activate A, and test 
that A did its thing. T considers B's behavior just a side-effect.

Most of the time, B should be real. And its state should be static (such as a 
Rails "fixture", with static data.) B should not have runaway dependencies. The 
test T should be easy to set-up.

You should mock B if it's too expensive to set up. For example, if B reads the 
system clock, and if T would prefer the date is 2008 July 31, the test T should 
not wait an infinite amount of time, until the cosmos oscillates and 2008 July 
31 occurs again. Tests should run as fast as possible, to avoid any hesitation 
running them.

You should mock B, so it behaves as if the date is correct. Or you should mock 
(in Ruby) Time.now, so all Ruby methods that use the date will read the mocked date.

Other examples of things too expensive to directly test:

  - live users
  - random numbers
  - hardware - networks, robots, tape drives, the clock, etc
  - system errors

If your B object is not on the list, you should not mock it. Unit tests work 
best when they cross-test everything. The only thing better than a test that 
fails because A broke is many tests that all accurately fail because B broke. If 
your B is too expensive to assemble, you should refactor it, so it bypasses the 
behavior that T and A did not care about.

> class Enterprise
>   def initialize(dilithium);  @dilithium = dilithium;  end
> 
>   def go(warp_factor);  warp_factor.times { @dilithium.nuke(:anti_matter) }; end
> end

Very nice. And note it obeys my list, by mocking both hardware and space-time 
distortions.

-- 
   Phlip

--PgMNv9sLzVj6sTwkBy-gCOqKoHn5pG6nUuRgNzL
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
</head>




<body style="background-color: #ffffff;">

<!--~-|**|PrettyHtmlStartT|**|-~-->
<div id="ygrp-mlmsg" style="width:655px; position:relative;">
<div id="ygrp-msg" style="width: 470px; margin:0; padding:0 25px 0 0; float:left; z-index:1;">
<!--~-|**|PrettyHtmlEndT|**|-~-->

    <div id="ygrp-text">
            <p>Shadowfirebird wrote:<br>
<br>
&gt; I've found this lovely bit of example code for mocha, though -- see<br>
&gt; below.  If I understand you correctly, you seem to be saying that you<br>
&gt; *don't* test the output routine; you use a mock to fake it so that you<br>
&gt; can test the rest of the code?<br>
<br>
Consider a test T that calls A, which calls B.<br>
<br>
Sometimes, a B is cheap to assemble, so T can assemble B, activate A, and test <br>
that A did its thing. T considers B's behavior just a side-effect.<br>
<br>
Most of the time, B should be real. And its state should be static (such as a <br>
Rails &quot;fixture&quot;, with static data.) B should not have runaway dependencies. The <br>
test T should be easy to set-up.<br>
<br>
You should mock B if it's too expensive to set up. For example, if B reads the <br>
system clock, and if T would prefer the date is 2008 July 31, the test T should <br>
not wait an infinite amount of time, until the cosmos oscillates and 2008 July <br>
31 occurs again. Tests should run as fast as possible, to avoid any hesitation <br>
running them.<br>
<br>
You should mock B, so it behaves as if the date is correct. Or you should mock <br>
(in Ruby) Time.now, so all Ruby methods that use the date will read the mocked date.<br>
<br>
Other examples of things too expensive to directly test:<br>
<br>
- live users<br>
  - random numbers<br>
  - hardware - networks, robots, tape drives, the clock, etc<br>
  - system errors<br>
<br>
If your B object is not on the list, you should not mock it. Unit tests work <br>
best when they cross-test everything. The only thing better than a test that <br>
fails because A broke is many tests that all accurately fail because B broke. If <br>
your B is too expensive to assemble, you should refactor it, so it bypasses the <br>
behavior that T and A did not care about.<br>
<br>
&gt; class Enterprise<br>
&gt;   def initialize(dilithiu<wbr>m);  @dilithium = dilithium;  end<br>
&gt; <br>
&gt;   def go(warp_factor)<wbr>;  warp_factor.<wbr>times { @dilithium.nuke(<wbr>:anti_matter) }; end<br>
&gt; end<br>
<br>
Very nice. And note it obeys my list, by mocking both hardware and space-time <br>
distortions.<br>
<br>
-- <br>
   Phlip<br>
</p>
    </div>  

    <!--~-|**|PrettyHtmlStart|**|-~-->
    <span width="1" style="color: white;">__._,_.___</span>
    <!-- Start the section with Message In topic -->
    <div id="ygrp-actbar">
              <span class="left">
          <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/message/1014;_ylc=X3oDMTM0ODIycDd2BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BG1zZ0lkAzEwMTQEc2VjA2Z0cgRzbGsDdnRwYwRzdGltZQMxMjE3NjQwMTU2BHRwY0lkAzEwMTQ-">
            Messages in this topic          </a> (<span class="bld">1</span>)
        </span>
        <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/post;_ylc=X3oDMTJwZzFlbzk2BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BG1zZ0lkAzEwMTQEc2VjA2Z0cgRzbGsDcnBseQRzdGltZQMxMjE3NjQwMTU2?act=reply&messageNum=1014">
          <span class="bld">
            Reply          </span> (via web post)
        </a>  | 
        <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/post;_ylc=X3oDMTJlaHNyaGxhBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTIxNzY0MDE1Ng--" class="bld">
          Start a new topic        </a>
          </div> 
    <!-------     Start Nav Bar  ------>
    <!-- |**|begin egp html banner|**| -->
    <div id="ygrp-vitnav">
                <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/messages;_ylc=X3oDMTJlYm1jMjE0BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA21zZ3MEc3RpbWUDMTIxNzY0MDE1Ng--">Messages</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/files;_ylc=X3oDMTJmZ2Jkdjc5BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2ZpbGVzBHN0aW1lAzEyMTc2NDAxNTY-">Files</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/photos;_ylc=X3oDMTJlbzFhdDY4BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3Bob3QEc3RpbWUDMTIxNzY0MDE1Ng--">Photos</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/links;_ylc=X3oDMTJmMW9xN3E3BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2xpbmtzBHN0aW1lAzEyMTc2NDAxNTY-">Links</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/database;_ylc=X3oDMTJjY2g4OHFiBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2RiBHN0aW1lAzEyMTc2NDAxNTY-">Database</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/polls;_ylc=X3oDMTJmaDYzZjNoBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3BvbGxzBHN0aW1lAzEyMTc2NDAxNTY-">Polls</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/members;_ylc=X3oDMTJlbzJzZmZqBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA21icnMEc3RpbWUDMTIxNzY0MDE1Ng--">Members</a>  
            |    <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/calendar;_ylc=X3oDMTJkNnFsajkyBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2NhbARzdGltZQMxMjE3NjQwMTU2">Calendar</a>  
    </div>  
    <!-- |**|end egp html banner|**| -->

                <div id="ygrp-grft">
                  

              </div>
    
    <!-- yahoo logo -->
    <!-- |**|begin egp html banner|**| -->
    <div id="ygrp-ft">
      <a href="http://groups.yahoo.com/;_ylc=X3oDMTJkYzBjb2JrBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2dmcARzdGltZQMxMjE3NjQwMTU2">
      <img src="http://us.i1.yimg.com/us.yimg.com/i/yg/img/logo/ma_grp_160.gif" height="15" width="106" border="0" alt="Yahoo! Groups"></a> <br>
      <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces/join;_ylc=X3oDMTJmYTc5cjBvBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA3N0bmdzBHN0aW1lAzEyMTc2NDAxNTY-">Change settings via the Web</a> (Yahoo! ID required) <br>
      Change settings via email: <a href="mailto:TestFirstUserInterfaces-digest-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=Email Delivery: Digest">Switch delivery to Daily Digest</a> | <a href = "mailto:TestFirstUserInterfaces-traditional-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=Change Delivery Format: Traditional">Switch format to Traditional</a> <br>

      <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces;_ylc=X3oDMTJkdnJyZnY4BF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwNmdHIEc2xrA2hwZgRzdGltZQMxMjE3NjQwMTU2">
        Visit Your Group 
      </a> |
      <a href="http://docs.yahoo.com/info/terms/">
        Yahoo! Groups Terms of Use      </a> |
      <a href="mailto:TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org?subject=">
        Unsubscribe      </a> 
    </div>     <!-- |**|end egp html banner|**| -->
  </div> <!-- ygrp-msg -->

  
  <!-- Sponsor -->
  <!-- |**|begin egp html banner|**| -->
  <div id="ygrp-sponsor" style="width:160px; float:right; clear:none; margin:0 0 25px 0; background:white;">
    <!-- Network content -->
    
<!-- Start Recommendations -->
<div id="ygrp-reco">
     </div>
<!-- End Recommendations -->

	    <!-- Start vitality -->
	    <div id="ygrp-vital">
	      	      <a href="http://groups.yahoo.com/group/TestFirstUserInterfaces;_ylc=X3oDMTJldm9jdHVtBF9TAzk3MzU5NzE0BGdycElkAzg3NDQ3MDQEZ3Jwc3BJZAMxNzA1MDA3MjA3BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTIxNzY0MDE1Ng--">
		Visit Your Group	      </a>
	    </div> 
	    	    	      
	    <!-- Network content -->
	    	          <div id="nc">
              <div class="ad">
                      <div id="hd1">Yahoo! Finance</div> 
<p><a href="http://us.ard.yahoo.com/SIG=13oa4mskp/M=493064.12016257.12445664.8674578/D=groups/S=1705007207:NC/Y=YAHOO/EXP=1217647357/L=/B=UqQrAULaX9k-/J=1217640157030172/A=4507179/R=0/SIG=12de4rskk/*http://us.rd.yahoo.com/evt=50284/*http://finance.yahoo.com/personal-finance">It's Now Personal</a></p> 
<p>Guides, news,</p> 
<p>advice & more.</p>                   </div>
                    <div class="ad">
                      <div id="hd1">Search Ads</div> 
<p><a href="http://us.ard.yahoo.com/SIG=13o54q4du/M=493064.12016308.12445700.8674578/D=groups/S=1705007207:NC/Y=YAHOO/EXP=1217647357/L=/B=U6QrAULaX9k-/J=1217640157030172/A=3848641/R=0/SIG=1312g85fq/*http://searchmarketing.yahoo.com/arp/srchv2.php?o=US2003&cmp=Yahoo&ctv=Groups2&s=Y&s2=&s3=&b=50">Get new customers.</a></p> 
<p>List your web site</p> 
<p>in Yahoo! Search.</p>                  </div>
                    <div class="ad">
                      <div id="hd1">Cat Groups</div> 
<p><a href="http://us.ard.yahoo.com/SIG=13oo2ufem/M=493064.12016263.12445670.8674578/D=groups/S=1705007207:NC/Y=YAHOO/EXP=1217647357/L=/B=VKQrAULaX9k-/J=1217640157030172/A=4836038/R=0/SIG=11olbte0b/*http://advision.webevents.yahoo.com/catzone/index.html">on Yahoo! Groups</a></p> 
<p>Share pictures &amp;</p> 
<p>stories about cats.</p>                  </div>
          </div>
	    
	  </div> 	  <!-- |**|end egp html banner|**| -->
	  <div style="clear:both; color: #FFF; font-size:1px;">.</div>
	</div> 		  <img src="http://geo.yahoo.com/serv?s=97359714/grpId=8744704/grpspId=1705007207/msgId=1014/stime=1217640156/nc1=4507179/nc2=3848641/nc3=4836038" width="1" height="1"> <br>
	
	<span  style="color: white;">__,_._,___</span>
	<!--~-|**|PrettyHtmlEnd|**|-~-->
	</body>
	<!--~-|**|PrettyHtmlStart|**|-~-->
	<head>
<style type="text/css">
<!--
#ygrp-mkp{
  border: 1px solid #d8d8d8;
  font-family: Arial;
  margin: 14px 0px;
  padding: 0px 14px;
}
#ygrp-mkp hr{
  border: 1px solid #d8d8d8;
}
#ygrp-mkp #hd{
  color: #628c2a;
  font-size: 85%;
  font-weight: bold;
  line-height: 122%;
  margin: 10px 0px;
}
#ygrp-mkp #ads{
  margin-bottom: 10px;
}
#ygrp-mkp .ad{
  padding: 0 0;
}
#ygrp-mkp .ad a{
  color: #0000ff;
  text-decoration: none;
}
-->
</style>
</head>
	<head>
<style type="text/css">
<!--
#ygrp-sponsor #ygrp-lc{
  font-family: Arial;
}
#ygrp-sponsor #ygrp-lc #hd{
  margin: 10px 0px;
  font-weight: bold;
  font-size: 78%;
  line-height: 122%;
}
#ygrp-sponsor #ygrp-lc .ad{
  margin-bottom: 10px;
  padding: 0 0;
}
-->
</style>
</head>
	<head>
	<style type="text/css">
	<!--
	#ygrp-mlmsg {font-size:13px; font-family: arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
	#ygrp-mlmsg table {font-size:inherit;font:100%;}
	#ygrp-mlmsg select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}
	#ygrp-mlmsg pre, code {font:115% monospace;*font-size:100%;}
	#ygrp-mlmsg * {line-height:1.22em;}
	#ygrp-text{
	    font-family: Georgia;	
	}
	#ygrp-text p{
	    margin: 0 0 1em 0;
	}
	#ygrp-tpmsgs{
	    font-family: Arial;	
	    clear: both;
	}
	#ygrp-vitnav{
		padding-top: 10px;
		font-family: Verdana;
		font-size: 77%;
		margin: 0;
	}
	#ygrp-vitnav a{
		padding: 0 1px;
	}
	#ygrp-actbar{
		clear: both;
		margin: 25px 0;
		white-space:nowrap;
		color: #666;
		text-align: right;
	}
	#ygrp-actbar .left{
		float: left;
		white-space:nowrap;
	}
	.bld{font-weight:bold;}
	#ygrp-grft{
		font-family: Verdana;
		font-size: 77%;
		padding: 15px 0;
	}
	#ygrp-ft{
	  font-family: verdana;
	  font-size: 77%;
	  border-top: 1px solid #666; 
	  padding: 5px 0; 
	}
	#ygrp-mlmsg #logo{
	  padding-bottom: 10px;
	}

	#ygrp-reco {
	margin-bottom: 20px;
	padding: 0px;
	}
	#ygrp-reco #reco-head {
		font-weight: bold;
		color: #ff7900;
	}

	#reco-grpname{
        font-weight: bold;
        margin-top: 10px;
  	}
	#reco-category{
        	font-size: 77%;
	}
	#reco-desc{
        	font-size: 77%;
	}

	#ygrp-vital{
		background-color: #e0ecee;
		margin-bottom: 20px;
		padding: 2px 0 8px 8px;
	}
	#ygrp-vital #vithd{
		font-size: 77%;
		font-family: Verdana;
		font-weight: bold;
		color: #333;
		text-transform: uppercase;
	}
	#ygrp-vital ul{
		padding: 0;
		margin: 2px 0;
	}
	#ygrp-vital ul li{
	  list-style-type: none;
	  clear: both;
	  border: 1px solid #e0ecee;  
	}
	#ygrp-vital ul li .ct{
	  font-weight: bold;
	  color: #ff7900;
	  float: right;
	  width: 2em;
	  text-align:right;
	  padding-right: .5em;
	}
	#ygrp-vital ul li .cat{
	  font-weight: bold;
	}
	#ygrp-vital a{
		text-decoration: none;
	}

	#ygrp-vital a:hover{
	  text-decoration: underline;
	}

	#ygrp-sponsor #hd{
		color: #999;
		font-size: 77%;
	}
	#ygrp-sponsor #ov{
		padding: 6px 13px;
		background-color: #e0ecee;
		margin-bottom: 20px;
	}
	#ygrp-sponsor #ov ul{
		padding: 0 0 0 8px;
		margin: 0;
	}
	#ygrp-sponsor #ov li{
		list-style-type: square;
		padding: 6px 0;
		font-size: 77%;
	}
	#ygrp-sponsor #ov li a{
		text-decoration: none;
		font-size: 130%;
	}
	#ygrp-sponsor #nc{
	  background-color: #eee;
	  margin-bottom: 20px;
	  padding: 0 8px;
	}
	#ygrp-sponsor .ad{
		padding: 8px 0;
	}
	#ygrp-sponsor .ad #hd1{
		font-family: Arial;
		font-weight: bold;
		color: #628c2a;
		font-size: 100%;
		line-height: 122%;
	}
	#ygrp-sponsor .ad a{
		text-decoration: none;
	}
	#ygrp-sponsor .ad a:hover{
		text-decoration: underline;
	}
	#ygrp-sponsor .ad p{
		margin: 0;
	}
	o{font-size: 0; }
	.MsoNormal{
	   margin: 0 0 0 0;
	}
	#ygrp-text tt{
	  font-size: 120%;
	}
	blockquote{margin: 0 0 0 4px;}
	.replbq{margin:4}
	-->
	</style>
	</head>
	<!--~-|**|PrettyHtmlEnd|**|-~-->
	</html><!--End group email -->


--PgMNv9sLzVj6sTwkBy-gCOqKoHn5pG6nUuRgNzL--