<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>I.M. Testy</title>
	<atom:link href="http://www.testingmentor.com/imtesty/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.testingmentor.com/imtesty</link>
	<description>Treatises on the practice of software testing</description>
	<lastBuildDate>Fri, 02 Dec 2011 01:48:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>API Testing&#8211;How can it help?</title>
		<link>http://www.testingmentor.com/imtesty/2011/12/01/api-testinghow-can-it-help/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/12/01/api-testinghow-can-it-help/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 01:48:41 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[General Testing Topics]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/12/01/api-testinghow-can-it-help/</guid>
		<description><![CDATA[After a rather wet and soggy weekend, I woke up this morning to a beautiful sunny day in Seattle. Despite it being a bit cool, I do enjoy the sunshine so much more than the dreary gray days of a Seattle winter. Most of the leaves have fallen from the trees which makes good mulch [...]]]></description>
			<content:encoded><![CDATA[<p>After a rather wet and soggy weekend, I woke up this morning to a beautiful sunny day in Seattle. Despite it being a bit cool, I do enjoy the sunshine so much more than the dreary gray days of a Seattle winter. Most of the leaves have fallen from the trees which makes good mulch for the gardens, but just adds more work to my stack. The good news is that there is snow in the mountains and the ski resorts in the area have opened early this year, so I hope to get in some good ski days.</p>
<p>In the previous post I attempted to explain the subtle differences between unit testing and API testing. It should also be noted that testing at the API layer is different than testing through the GUI. API testing is primarily focused on the functionality of the business logic of the software, and not necessarily the behavior or the “look and feel” from the end user customer perspective. In fact, the 1st tier customer of the API tester is the developer who designed and develops the API. The second tier customers are the developers who utilize the APIs in building the user interface or applications that sit on top of the underlying infrastructure of a program. And finally, API testers must also consider the various end-to-end scenarios of the end user customers at the integration level of testing (without a GUI).</p>
<p>This post will discuss why API testing is an important activity in the complete software development lifecycle (SDLC). Teams that have multiple developers and a continuous integration (CI) build process can greatly benefit from API testing. Key benefits of API testing include:</p>
<ul>
<li>Reduced testing costs </li>
<li>Improved productivity </li>
<li>Higher functional (business logic) quality&#160;&#160;&#160; </li>
</ul>
<h4>Reduced Testing Costs</h4>
<p>It shouldn’t be a surprise to anyone that finding most types of functional bugs early in the SDLC is more cost effective. The primary goal of unit testing, and component/integration levels of testing (API testing) is to flush out as many functional issues in the business logic layer of a software program as early as possible in the SDLC. Driving functional quality upstream not only reduces production costs, but can also reduce testing costs.</p>
<p>API testing can reduce the overall cost of test automation.&#160; Automated API tests are based on the API’s interface. So, once the API interface is defined testers can begin to design and develop automated tests. Having a battery of automated tests ready as the functional APIs come on-line pushes testing upstream in parallel with development rather than later in the SDLC. This also enables earlier tester engagement and closer collaboration between testers and developers.</p>
<p>Also, since API interfaces are generally very stable, so automated API tests are less impacted by changes as compared to GUI based automated tests. Many testers are familiar with the constant upkeep and maintenance typically associated with GUI based automated tests. The constant massaging of GUI automation is often a huge cost in a test automation effort and a contributing factor to why so many automation projects fail. Automated API tests in general require a lot less maintenance unless there is a fundamental change in the underlying infrastructure or design of the program.</p>
<p>Another significant way that API testing can reduce testing costs is by refocusing testing. Many test strategies rely&#160; heavily on finding functional bugs typically using exploratory type testing through the GUI. But, most software produced today is developed in “layers” (see Testing in Layers). A more robust test strategy should focus the bulk of functional testing at the API layer that contains the “business logic” of the program. Of course some functional issues will still be found while testing through the GUI, but the focus of testing at the GUI layer should be on&#160; behavioral testing. A test strategy that provides a multi-tiered approach is more effective than the typical approach of throwing a bunch of bodies to bang on the GUI in an attempt to beat out the bugs. A multi-tiered test strategy may even reduce the total testing time by reducing the need to spend long cycles trying to uncover a lot of functional bugs through the GUI.</p>
<h4>Improved Productivity</h4>
<p>There are different ways to evaluate productivity, but certainly one way is to ensure production keeps moving forward. Continuous integration is a keystone of Agile development projects, and at Microsoft this means daily builds. If the build breaks, production grinds to a virtual halt and forward momentum is blocked until the issue is fixed. A build break negatively impacts the productivity of the entire team. A suite of low level integration tests can help identify potential build breaks especially involving dependent modules before new fixes or features are merged into a higher level branch.</p>
<p>API testing can also improve productivity of testing. For example, structural testing is a white box test approach intended to test the structure or flow of a program. If increased levels of code coverage is an important goal, then the most efficient way to improve structural coverage is to identify untested code paths and design and develop API level tests that will tactically target untested code.</p>
<p>Perhaps the most significant improvement to productivity is gained through teamwork. Building and releasing great software products require a team effort. A team of people working closely together. Gone are the days of the adversarial relationship between developers and testers. Changes in technologies, changes in customer demands, and changes in how we build software require close collaboration between developers and testers, and testers being actively engaged throughout the SDLC and not just at the beginning (picking apart a spec), or at the end (banging out&#160; bugs via the GUI pretending to mimic a ‘customer’). A team focused on delivering high quality can greatly add to a team’s overall productivity.</p>
<h4>Higher Functional Quality</h4>
<p>One of the advantages and also disadvantages of testing at the API layer is that you can test the API in ways that are different then how the GUI interacts with the API. For example, the Morse Code Trainer has an interface for the methods that parse the dots and dashes and plays a system beep of 1 unit duration for each dot in the stream, and a system beep for 3 units of duration for each dash in the stream. The duration of a unit is based on the WordsPerMinute property value. </p>
<pre>   1:    interface ISoundGenerator</pre>
<pre>   2:    {</pre>
<pre>   3:      void PlayMorseCode(string morseCodeString);</pre>
<pre>   4:   </pre>
<pre>   5:      int WordsPerMinute { get; set; }</pre>
<pre>   6:    }</pre>
<pre>&#160;</pre>
<p>Testing this property at the API level we could “set” a negative integer value to make sure nothing really bad happens. But, a well-designed GUI would never accept an integer value less than 1 (which is painfully slow) nor above 150 (which is ridiculously high). A better design might be to use a drop-down list of values ranging from 5 (the minimum requirement for a basic license) to 20 words per minute (required for the highest level amateur radio operator license). Of course, it may be possible to find functional anomalies while API testing that could not be found via testing through the GUI. But, the important thing an API tester must consider is how a bug found at the component or integration levels of testing adversely affects a scenario, or the customer.</p>
<p>API testers work alongside of the developers. An API tester may also provide input into the initial API design, engage in code reviews before check-ins, and of course write automated tests to test the API (component level) and APIs in end-to-end scenarios (integration level). Having testers engage with developers early and throughout the SDLC helps ensure team work and instills the idea that quality is a collaborative effort.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/12/01/api-testinghow-can-it-help/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>API Testing&#8211;Functional Testing Below the User Interface</title>
		<link>http://www.testingmentor.com/imtesty/2011/11/21/api-testingfunctional-testing-below-the-user-interface/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/11/21/api-testingfunctional-testing-below-the-user-interface/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 05:47:27 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[General Testing Topics]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/11/21/api-testingfunctional-testing-below-the-user-interface/</guid>
		<description><![CDATA[After a long hiatus from writing I am finally carving out some time to put thoughts to words again. A lot has been going on both professionally and personally. On the personal side I will simply say that never take for granted the time someone you care for has on this earth, and make a [...]]]></description>
			<content:encoded><![CDATA[<p>After a long hiatus from writing I am finally carving out some time to put thoughts to words again. A lot has been going on both professionally and personally. On the personal side I will simply say that never take for granted the time someone you care for has on this earth, and make a habit of spending quality time with that person regularly. On the professional side, things have been crazy busy in a very good way. I have settled in at work (still have lots to learn as always), and squeezed out a day to drive to Portland, OR to <a href="http://www.pnsqc.org/2011-conference/papers-and-presentations" target="_blank">speak at PNSQC on random test data generation</a>, and also present an online presentation discussing API testing best practices for the <a href="http://www.softwaretestpro.com/Event/1138" target="_blank">STP Online Summit: Achieving Business Value With Test Automation</a>. Based on questions from that session I thought I would follow up with a few posts discussing API testing. Let’s start with describing API testing and how it differs from other “<a href="http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/" target="_blank">levels of software testing</a>.”</p>
<h4>Application Programming Interface (API)</h4>
<p>The <a href="http://www.microsoft.com/learning/en/us/book.aspx?ID=5582&amp;locale=en-us" target="_blank">Microsoft Press Computer Dictionary</a> defines API as “A set of routines used by an application program to direct the performance of procedures by the computer&#8217;s operating system.” So, referring to the abstract levels of testing, an API can be a unit, but is more likely a component because it is usually “an integrated aggregate of one or more units.”</p>
<p>An API provides value to both the developer and to the customer. For example, an API: </p>
<ul>
<li>provides developers with common reusable functions so they don’t have to rewrite common routines from scratch </li>
<li>provides a level of abstraction between the application and lower level ‘privileged’ functions </li>
<li>ensures any program that uses a given API will have identical behavior/functionality (<em>for example, many Windows programs use a common file dialog such as <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb776913(v=vs.85).aspx" target="_blank">IFileSaveDialog</a> API to allow customers to save files in a consistent manner</em>) </li>
</ul>
<p>Essentially, an API contains the core functionality of a program, or the business logic as some people refer to it. Customers don’t interact with API’s directly. Customers interact with software via the Graphical User Interface (GUI) which in turn interacts with an abstraction layer (e.g. controller in the MVC design pattern) that interacts with APIs exposed via <a href="http://en.wikipedia.org/wiki/Interface_(computer_science)" target="_blank">Interfaces</a>. </p>
<h4>Testing an API as a Black Box</h4>
<p>Some people assume that API testing is a ‘white-box’ testing activity in which the tester has access to the product source code. But in reality, API testing is truly black-box testing in the truest sense of the testing approach. API testers make no assumptions about how the functionality is implemented, and are not limited by constraints or distracting behaviors of a graphical user interface. </p>
<p>As an example, let’s use a program I developed called Morse Code Trainer. As a boy I was really into electronics (HeathKit projects were routinely on my Christmas list), and in order to pursue my amateur radio license I had to learn Morse code, or CW for short. Although Morse code is not required any longer to get a HAM operators license I think it is not hard to learn (memorize) about 55 sequences of dits and dahs, and in my opinion learning additional languages is good for the brain.</p>
<p>A core bit of functionality in this program is to convert a string of characters (a sentence) to the dits (represented as a period character “.”) and dahs (represented as a dash character “-“). The API to do this bit of magic is: </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="#0000ff">string</font> AlphaNumericCharacterStringToMorseCodeString(<font color="#0000ff">string</font> input)</p>
<p>and is exposed to the developer who will code the UI and controller via the IMorseCodeEncoder interface.</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="#0000ff">interface</font> IMorseCodeEncoder     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="#0000ff">string</font> AlphaNumericCharacterStringToMorseCodeString(<font color="#0000ff">string</font> input); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <font color="#0000ff">string</font> MorseCodeStringToAlphaNumericCharacterString(<font color="#0000ff">string</font> input);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>Notice that we don’t see any of the underlying code of how this method actually does its magic. </p>
<p>Let’s assume the developer didn’t do any unit testing and simply threw the code over the proverbial wall for testers to beat on. Since the tester (me) knows the developer (me) didn’t do any unit testing of any of the private methods the API under test relies on, the API tester (me) writes a simple test just to see if this code “works” like the developer (me) assured the tester (me) that it would. The most basic API test looks very similar to the unit test illustrated below, and in fact it this is a unit test the developer should write and execute before chucking a program at testers to bang on. A proper API test would call the method under test from the dynamic link library (DLL), and include initialization, clean-up, utilize the proper test design, have a robust oracle,&#160; and of course have no hard-coded strings. </p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span>    [TestMethod()]</pre>
<pre><span class="lnum">   2:  </span>    [DeploymentItem(<span class="str">&quot;Morse Code Trainer.exe&quot;</span>)]</pre>
<pre><span class="lnum">   3:  </span>    <span class="kwrd">public</span> <span class="kwrd">void</span> GetMorseCodeStreamTest()</pre>
<pre><span class="lnum">   4:  </span>    {</pre>
<pre><span class="lnum">   5:  </span>      <span class="kwrd">try</span></pre>
<pre><span class="lnum">   6:  </span>      {</pre>
<pre><span class="lnum">   7:  </span>        MorseCodeEncoder_Accessor target = <span class="kwrd">new</span> MorseCodeEncoder_Accessor();</pre>
<pre><span class="lnum">   8:  </span>        <span class="kwrd">string</span> input = <span class="str">&quot;A QUICK TEST&quot;</span>;</pre>
<pre><span class="lnum">   9:  </span>        <span class="kwrd">string</span> expected = <span class="str">&quot;.-  --.- ..- .. -.-. -.-  - . ... -&quot;</span>;</pre>
<pre><span class="lnum">  10:  </span>        <span class="kwrd">string</span> actual;</pre>
<pre><span class="lnum">  11:  </span>        actual = target.GetMorseCodeStream(input);</pre>
<pre><span class="lnum">  12:  </span>        Assert.AreEqual(expected, actual);</pre>
<pre><span class="lnum">  13:  </span>      }</pre>
<pre><span class="lnum">  14:  </span>      <span class="kwrd">catch</span> (Exception e)</pre>
<pre><span class="lnum">  15:  </span>      {</pre>
<pre><span class="lnum">  16:  </span>        Assert.Fail(e.ToString());</pre>
<pre><span class="lnum">  17:  </span>      }</pre>
<pre><span class="lnum">  18:  </span>    }</pre>
</div>
<p>&#160;</p>
<p>Interestingly enough, had the developer (me) ran this unit test the developer would have discovered an unhandled exception. The unit test failed because this API called a method to get a <a href="http://msdn.microsoft.com/en-us/library/xfhwa508.aspx" target="_blank">Dictionary</a> in another class and the Dictionary was created from 2 string arrays (an array of alpha-numeric characters, and an array of Morse code sequences). The specific error was a duplicate key/value in the Dictionary; in other words a duplicate entry in the <em>alphaCharacterArray</em> string array was throwing an System.ArgumentException for duplicate keys. But, because the methods in the MorseCodeLibrary class weren’t unit tested the API to encode a string of alpha-numeric characters to Morse code characters failed its basic unit test.</p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span>    <span class="kwrd">public</span> Dictionary&lt;<span class="kwrd">string</span>, <span class="kwrd">string</span>&gt; GetAlphaCharacterToMorseCodeDictionary()</pre>
<pre><span class="lnum">   2:  </span>    {</pre>
<pre><span class="lnum">   3:  </span>      Dictionary&lt;<span class="kwrd">string</span>, <span class="kwrd">string</span>&gt; AlphaToMorseCodeDictionary = <span class="kwrd">new</span> Dictionary&lt;<span class="kwrd">string</span>, <span class="kwrd">string</span>&gt;();</pre>
<pre><span class="lnum">   4:  </span>      <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i &lt; <span class="kwrd">this</span>.alphaCharacterArray.Length; i++)</pre>
<pre><span class="lnum">   5:  </span>      {</pre>
<pre><span class="lnum">   6:  </span>        AlphaToMorseCodeDictionary.Add(<span class="kwrd">this</span>.alphaCharacterArray[i], <span class="kwrd">this</span>.morseCodeArray[i]);</pre>
<pre><span class="lnum">   7:  </span>      }</pre>
<pre><span class="lnum">   8:  </span>&#160;</pre>
<pre><span class="lnum">   9:  </span>      <span class="kwrd">return</span> AlphaToMorseCodeDictionary;</pre>
<pre><span class="lnum">  10:  </span>    }</pre>
</div>
<style type="text/css">
<p>.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>&#160;</p>
<p>But, it actually gets worse. Another API in another class to decode a string of Morse code to alpha-numeric characters would have failed as well because it used the same faulty string array of data to create a Dictionary calling the public method </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="kwrd"><font color="#0000ff">public</font></span> Dictionary&lt;<span class="kwrd"><font color="#0000ff">string</font></span>, <span class="kwrd"><font color="#0000ff">string</font></span>&gt; <em>GetMorseCodeToAlphaCharacterDictionary</em>().</p>
<blockquote>
<p><strong><em>This is actually a good example of 2 very different bugs with the same root cause. This is also good example of how it is more efficient to find functional bugs at the unit,&#160; or component or integration levels of testing (API testing) as compared to finding this problem via functional testing through the user interface.<br />
<style type="text/css">
<p>.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p></em></strong></p>
</blockquote>
<h4>Unit vs. API Testing</h4>
<p>So, you’re probably asking yourself “if the above example is really an example of a unit test the developers should do before throwing their code at testers, then how does unit testing differ from API testing?” When testing a single API call the most significant difference is in the thoroughness of test coverage. Most unit tests are rather simple things. Unit tests are not very complex; unit tests are not comprehensive in test coverage (although a good suite of unit tests should achieve good structural coverage); and unit tests often rely on simplistic oracles.</p>
<p>API tests by contrast are usually more comprehensive as compared to unit tests. API tests usually include both positive tests (does it do what its supposed to do) as well as negative tests (how well does it handle error conditions). While API tests should strive for a high level of code coverage (structural testing) a more important goal is test coverage. For example API tests of this same method might include a series of data-driven tests that:</p>
<ul>
<li>test every known alpha-numeric character defined in Morse code (<em>the population of the variable is small enough to test every element, if the population of a given variable is large then testers should define equivalent partitions and test an adequate number of samples from the population for confidence</em>) </li>
<li>test character casing </li>
<li>test pangrams, and special signals (<em>e.g. end of message, attention, received, etc</em>) </li>
<li>test boundary conditons (<em>e.g string max len although 2 billion+ characters seems excessive for a Morse code transmission</em>) </li>
<li>test strings with invalid or characters that are not defined in Morse code </li>
<li>test strings with non-ASCII letters that have Morse code encodings (Ä, Á, Å, Ch, É, Ñ, Ö, Ü) </li>
<li>test performance to provide baseline measures of individual methods </li>
</ul>
<p>More complex APIs such as this <a href="http://msdn.microsoft.com/en-us/library/ba3x8zfh.aspx" target="_blank">MessageBox.Show</a> method that have several parameters with variable argument values might benefit from additional testing techniques such as combinatorial testing.</p>
<h4>Testing API End–To–End Scenarios</h4>
<p>Testing a single API is usually considered unit or component level testing in the <a href="http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/" target="_blank">abstract levels of testing</a>. Some people consider unit and component level testing to be “owned” by the developer. I certainly agree that unit tests must be owned by the developer, and that developers can do a much better job of component level testing. But, I also think this is a key area where API testers can collaborate more closely with developers to increase the effectiveness of the tests, the data used in the test, and even the test design (e.g. data-driven unit testing). </p>
<p>But, I will suggest that the integration level of testing or “testing done to show that even though the components were individually satisfactory, as demonstrated by successful passage of component tests, the combination of components are incorrect or inconsistent“ is the domain of the API tester. Software applications are complex beasts that often rely on sequences of API calls interacting with databases, cloud services, or other background workers. So, although API testing rarely involves testing through the GUI, API testers must also understand how the various APIs will be used to effect various customer scenarios. The only difference is that API testers emulate these scenarios without navigating a graphical user interface.</p>
<p>For example, one scenario is to convert a string of text into dits and dahs and use the system’s beep to “play” the Morse code sequence over the computer’s speaker. So, this program contains a class to convert the sequences of dits and dahs into sound; the SoundGenerator class. The interface for the sound functions includes a getter and setter, and the PlayCharacterCode API.</p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span>  <span class="kwrd">interface</span> ISoundGenerator</pre>
<pre><span class="lnum">   2:  </span>  {</pre>
<pre><span class="lnum">   3:  </span>    <span class="kwrd">void</span> PlayMorseCode(<span class="kwrd">string</span> morseCodeString);</pre>
<pre><span class="lnum">   4:  </span>&#160;</pre>
<pre><span class="lnum">   5:  </span>    <span class="kwrd">int</span> WordsPerMinute { get; set; }</pre>
<pre><span class="lnum">   6:  </span>  }</pre>
</div>
<style type="text/css">
<p>.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>&#160;</p>
<p>So, although we don’t know exactly yet how the developer and GUI designer will implement the GUI for this program, we can still create a test that inputs a string of alphanumeric characters, encodes the alphanumeric string into a Morse code encoded string, and then passes the string of Morse code dits and dahs as an argument to the PlayMorseCode method. This is a rather simple example of an end-to-end scenario. In more complex application the API functions/methods would likely be compiled in one or more dynamic link libraries (DLLS), rely on mocks, fake servers, and possibly other emulators. Of course, the oracles for this type of API testing is also more complex and generally involves checking multiple outcomes or states.</p>
<p>API testing focuses on an application’s functional capabilities, whereas testing through a GUI should focus primarily on behavior, usefulness and general ‘likeability.’ </p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/11/21/api-testingfunctional-testing-below-the-user-interface/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Decoding the Secrets in Unicode Strings</title>
		<link>http://www.testingmentor.com/imtesty/2011/09/06/decoding-the-secrets-in-unicode-strings/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/09/06/decoding-the-secrets-in-unicode-strings/#comments</comments>
		<pubDate>Tue, 06 Sep 2011 17:40:01 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[Internationalization Testing]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/09/06/decoding-the-secrets-in-unicode-strings/</guid>
		<description><![CDATA[At the end of each week one of the last things I do is open my junk mail folder in Outlook and check to see if an email was moved there inadvertently before deleting all the spam that let’s me know that I’ve won the lottery in Ethiopia, or that my long lost relative in [...]]]></description>
			<content:encoded><![CDATA[<p>At the end of each week one of the last things I do is open my junk mail folder in Outlook and check to see if an email was moved there inadvertently before deleting all the spam that let’s me know that I’ve won the lottery in Ethiopia, or that my long lost relative in Chechnya left me 19 bazillion Euros, or the countless discount drug offerings. So, as I was going through my Friday evening spam mail deletion ritual I noticed a subject line that was a bit unusual. Before you jump to any incorrect conclusions it wasn’t about appendage enlargement, or free internet dating services. The email title was in Arabic, but included a “box” character at the beginning of the string.</p>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image_thumb.png" width="677" height="99" /></a></p>
<p>Now, I don’t read Arabic, but I am pretty good at noticing globalization bugs when they are staring me right in the face. The “box” character (actually a glyph) in a Unicode string either represents an Unicode code point that is unassigned (it doesn’t have a character associated with that code point value), or the system doesn’t have a font that maps a glyph (the character we see) to that particular Unicode code point. So, curiosity got the better of me, and I decided to investigate a bit. The first thing I did was to right click on the email subject line and paste it into Notepad and notice that the “box” glyph did not appear. </p>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image1.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image_thumb1.png" width="692" height="79" /></a></p>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image2.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" align="left" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/09/image_thumb2.png" width="301" height="457" /></a>A few years ago I developed a utility for decoding Unicode Strings aptly called “<a href="http://www.testingmentor.com/tools/generaltools.htm" target="_blank">String Decoder</a>” and also wrote <a href="http://www.testingmentor.com/imtesty/2009/11/18/troubleshooting-test-data-with-string-decoder/" target="_blank">a post</a> that discusses the tool. So, I launched String Decoder and copied the Arabic string from Notepad and pasted it into the String Decoder tool. </p>
<p>The first thing I notice when reading through the list of Unicode code point values is the value U+FEFF. Now, I happen to know that this particular value is a <a href="http://www.unicode.org/faq/utf_bom.html#bom1" target="_blank">byte order mark (BOM)</a>. This seems pretty unusual and ask myself how a BOM character could get inserted in a string. So, I look up the character in the <a href="http://www.unicode.org/charts/" target="_blank">Unicode Charts</a> and discover that in the Arabic Presentation Forms-B character set this was a special character for a zero width no-break space that as been deprecated. Ah, so the Unicode BOM code point value appearing in the string is not so magical after all! </p>
<p>Interestingly enough, the U+FEFF character only displays as a “box” glyph in the subject line in the Junk E-mail folder. When I copied the email message from the Junk folder to my Inbox (or other folder) the code point U+FEFF is treated as a zero width non-breaking space character so no box glyph appears. This is due to the fact that when an email gets shunted into the Junk E-mail folder “links and other functionality have been disabled.” In other words, it is plain-text.</p>
<p>I previously also wrote about <a href="http://www.testingmentor.com/imtesty/2011/01/23/a-source-of-real-world-test-data-for-globalization-testing/" target="_blank">using “real world” test data for globalization testing</a>, and this is another example of “real-world” data can be useful in testing text inputs and outputs to evaluate how unexpected character code points in a string are parsed or handled. I think this also bolsters the argument to include some amount of test data randomization using tools such as the <a href="http://www.testingmentor.com/tools/tool_pages/babel.htm" target="_blank">Babel tool</a> in globalization testing to potentially test for other unexpected characters or sequences of mixed Unicode characters.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/09/06/decoding-the-secrets-in-unicode-strings/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>More Thoughts on Leadership</title>
		<link>http://www.testingmentor.com/imtesty/2011/08/27/more-thoughts-on-leadership/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/08/27/more-thoughts-on-leadership/#comments</comments>
		<pubDate>Sat, 27 Aug 2011 23:01:05 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[Test Management]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/08/27/more-thoughts-on-leadership/</guid>
		<description><![CDATA[I have been in my new role as Test Lead 6 months now. The experience has been magnified because I am actually leading 2 platform teams; the social networking integration team, and the models team. The learning curve has been exponential. In my transition to this role I took advantage of attending a few HR [...]]]></description>
			<content:encoded><![CDATA[<p>I have been in my new role as Test Lead 6 months now. The experience has been magnified because I am actually leading 2 platform teams; the social networking integration team, and the models team. The learning curve has been exponential. In my transition to this role I took advantage of attending a few HR courses to refresh my knowledge in management principles. I also read quite a few books. Perhaps the single book that I read that helped reinforce my ideas of leadership (outlined in <a href="http://www.testingmentor.com/imtesty/2009/11/18/thoughts-on-leadership/" target="_blank">this blog post</a>) was <em><a href="http://www.amazon.com/Mentor-Leader-Secrets-Building-Consistently/dp/141433804X" target="_blank">The Mentor Leader: Secrets to Building People and Teams that Win Consistently</a></em> by Tony Dungy. This is a great book for leads/managers and anyone who mentors others.</p>
<p>If you ask any lead they will likely agree that their success as a lead hinges largely on their team. But, if you ask leads what their first priority is they will likely say shipping a product, or managing testing of some feature area they have been assigned. Yes, ultimately we need to ship a product and do our best to make sure our feature areas are adequately tested in an attempt to improve our customer’s overall experience. There are many ‘managers’ throughout the industry who are good at manipulating ‘resources’ to achieve some desired result or filling in magic numbers on a balanced scorecard. Balanced scorecards provide some value to a business, but sometimes managers lose sight of what is most important and focus on doing mundane things that will twiddle the numbers to make it fit into the scorecard to hype success. But, managing resources to ship a product is different than leading a team of people to achieve, and sometimes exceed goals and visions. </p>
<p>Leadership is much more than management. A successful leader manages projects by articulating a clear vision, guiding people towards achieving goals, and motivates people by helping them grow. When folks ask me what my first priority is as a Test Lead I say it is the people on my team. But, what does that mean? </p>
<h4>Open doorways to dreams!</h4>
<p>One of my primary responsibilities as a lead is to help the people on my team grow and expand their scope of influence and impact not only on my team, but ultimately within other teams across Microsoft. Of course it is always hard to see someone on our team leave for new opportunities, but good leaders understand the career aspirations of the people on the team and work with them to help them achieve those dreams. Leaders find opportunities that will help people develop skills that will benefit both the project and the person. Leaders should be truly invested and take an active role in helping people on their team grow even if that means the person will eventually leave the team to find new challenges. Managers fear losing the people on their team; leaders nurture people on their teams and open doorways to dreams and new opportunities. Think of it this way, would you rather join a team in which the manager holds on to people until they burn out, or a team in which the leader has a track-record of helping people grow into their next job.</p>
<h4>Delegate responsibility not just work!</h4>
<p>Like many other leaders, I have many balls to juggle, and I can’t juggle them all alone. So, as leads we must delegate some of the things on our plates. But, delegation is more than assigning tasks to people. Delegation is endorsing people on your team who will represent you have be responsible for driving a project that has a broad scope of impact. Of course, delegation also doesn’t mean just throwing ideas out there and seeing what happens. A leader who delegates work will set clear expectations and realistic goals, coach for success, provide guidance on how to build upon success, and perhaps most importantly empower the person to make decisions on their own. When we delegate we should set people up for success; not throw them into the fire of failure.</p>
<h4>Encourage risk and accept failure!</h4>
<p>Sometimes when people know that I am an avid sailor they will ask me to teach them to sail. I love sharing knowledge and experiences about things that I am passionate about with people who are interested in learning. Sometimes people are hesitant to do something because they don’t want to break something, or do something wrong. I make it very clear from the start that every inanimate object on the boat is replaceable, and while back-winding a sail means we steered too far into the wind it can always be corrected. I know many ‘captains’ who yell and shout when a line gets twisted and jams in a sheave, or someone accidentally releases the main halyard while under sail. It’s our reactions to such situations that provide a positive learning experience or turn our experience into a day of hell on the water. Leaders encourage people to try new things, innovate, and experiment. Leaders also know that sometimes things might not work out perfectly and should be willing to protect people from harm (either physical harm on the boat, or professional/political harm at work), and rebuild a persons confidence when things don’t work out so well.</p>
<h4>The burden of blame!</h4>
<p>At the end of the day I can’t point fingers and say “so and so didn’t do such and such,” or “if things weren’t so screwed up to begin with we wouldn’t be in this mess.” As a lead I am accountable. If things go wrong I first look at my own leadership to see if I failed to set clear expectations, or neglected to provide adequate guidance (without hand-holding), or did I “delegate and disappear.” Ultimate the responsibility of achieving my team’s goals and objectives is mine. We succeed as a team, or I fail as an individual.</p>
<h4>Attitude adjustment!</h4>
<p>I sometimes see managers who are grumpy or apathetic. I sometimes hear managers say, “I don’t like this either, but we have to do it to satisfy some other manager or scorecard criteria.” A good leader understands asks and explains why and how they might provide value to the requestor. I know that my attitude affects the people on my team, and if I appear empathetic towards their ideas then they will likely not share their innovative ideas with me. If I am constantly complaining about something, then my team learns to complain about similar things and we start to look like a bunch of whiners. (Nobody really likes whiners. People might try to appease whiners from time to time, but ultimately they just want the whiners to go away.) Leaders know they are being watched and should always project a positive attitude.</p>
<p>So, after 6 months of being back in the trenches, shipping a product, and facing some tough challenges I will say that I am still loving it!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/08/27/more-thoughts-on-leadership/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Dealing with locale/language specific static test data</title>
		<link>http://www.testingmentor.com/imtesty/2011/08/21/dealing-with-localelanguage-specific-static-test-data/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/08/21/dealing-with-localelanguage-specific-static-test-data/#comments</comments>
		<pubDate>Mon, 22 Aug 2011 07:17:06 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[Internationalization Testing]]></category>
		<category><![CDATA[Test Automation]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/08/21/dealing-with-localelanguage-specific-static-test-data/</guid>
		<description><![CDATA[It has been sometime since my last post. This seems to happen every so often lately; not because I don’t have anything to write about but mostly due to having too many irons in the fire so to speak and juggling hot irons is never fun and one is always going to drop. Also about [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/Photo_8E4D6B91-9BD2-E46E-F9EB-0E718B64C8E1.jpg"><img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Photo_8E4D6B91-9BD2-E46E-F9EB-0E718B64C8E1" border="0" alt="Photo_8E4D6B91-9BD2-E46E-F9EB-0E718B64C8E1" align="left" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/Photo_8E4D6B91-9BD2-E46E-F9EB-0E718B64C8E1_thumb.jpg" width="224" height="301" /></a><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/Photo_EA7B694A-5664-CD3C-B691-B859E85F742C.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top: 0px; border-right: 0px; padding-top: 0px" title="Photo_EA7B694A-5664-CD3C-B691-B859E85F742C" border="0" alt="Photo_EA7B694A-5664-CD3C-B691-B859E85F742C" align="right" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/Photo_EA7B694A-5664-CD3C-B691-B859E85F742C_thumb.jpg" width="222" height="298" /></a>It has been sometime since my last post. This seems to happen every so often lately; not because I don’t have anything to write about but mostly due to having too many irons in the fire so to speak and juggling hot irons is never fun and one is always going to drop. Also about this time every year I go sailing in the San Juan Islands or the Gulf Islands of British Columbia. This year I went to the San Juan Islands, and spent a few days incommunicado anchored in Shallow Bay on Sucia Island. Sucia. Echo Bay is a great anchorage with sandy beaches (unusual for the PNW), and the famous China Caves to explore.</p>
<p>Another place I have been known to explore from time to time is the <a href="http://sqa.stackexchange.com/" target="_blank">Stack Exchange Software Quality Assurance and Testing</a> forum. There are many interesting questions and a great variety of responses that offer a wealth of information or provide different perspectives. Recently a question was posed about how to read in static test data for a specific locale or language. Many regular readers know that I am a strong proponent of pseudo-random test data generation in conjunction with automated testing to increase the variability of test data used in each test iteration and generally improve test coverage. But I also understand the value of static test data in providing a solid baseline, and in some cases enabling access to specific test data in different locales or languages. </p>
<p>For example, suppose I am testing a text editor application and I want to read in a text file in the appropriate language based on the operating system current users locale settings. In this situation, I could save a text file containing strings or sentences for each target language or locale dialect. Each file would get a unique name based on the 3 letter ISO-639-2 language name (the complete list is at <a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">http://www.loc.gov/standards/iso639-2/php/code_list.php</a>), prepended it to a common filename that describes the contents and the appropriate extension. For example,</p>
<ul>
<li>ENG[TestData].txt would be English</li>
<li>ZHO[TestData].txt would be Chinese</li>
<li>DEU[TestData].txt would be German</li>
</ul>
<p>To get the appropriate text file auto-magically read in to the test at runtime the only thing we would need to do is to get the current user locale using the <a href="http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx" target="_blank">CultureInfo</a> class <a href="http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.threeletterisolanguagename.aspx" target="_blank">Three Letter ISO Language Name property</a> in C#.</p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span>            <span class="kwrd">string</span> testDataFileName = <span class="str">&quot;testdata.txt&quot;</span>;</pre>
<pre><span class="lnum">   2:  </span>&#160;</pre>
<pre><span class="lnum">   3:  </span>            CultureInfo ci = CultureInfo.CurrentCulture;</pre>
<pre><span class="lnum">   4:  </span>&#160;</pre>
<pre><span class="lnum">   5:  </span>            <span class="rem">// Path to server location where static files exist </span></pre>
<pre><span class="lnum">   6:  </span>            <span class="kwrd">string</span> path = Path.GetFullPath(</pre>
<pre><span class="lnum">   7:  </span>                Environment.GetFolderPath(Environment.SpecialFolder.Desktop));</pre>
<pre><span class="lnum">   8:  </span>&#160;</pre>
<pre><span class="lnum">   9:  </span>            <span class="rem">// Read file contents</span></pre>
<pre><span class="lnum">  10:  </span>            <span class="kwrd">using</span> (StreamReader readFile = </pre>
<pre><span class="lnum">  11:  </span>                <span class="kwrd">new</span> StreamReader(Path.Combine(</pre>
<pre><span class="lnum">  12:  </span>                    path, <span class="kwrd">string</span>.Concat(</pre>
<pre><span class="lnum">  13:  </span>                    ci.ThreeLetterISOLanguageName, testDataFileName))))</pre>
<pre><span class="lnum">  14:  </span>            {</pre>
<pre><span class="lnum">  15:  </span>                <span class="rem">//parse test data and do test stuff</span></pre>
<pre><span class="lnum">  16:  </span>            }</pre>
</div>
<p>&#160;</p>
<p>Notice we concatenate the filename (and extension) and the 3-letter ISO language name in line 13 and then combine that with the path to the file location and read the file contents using StreamReader.<br />
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
</p>
<p>But, we might need more specialization depending on what we are testing. For example, if we were testing a spell checker for US versus Great Britain (and Canada), or <a href="http://people.w3.org/rishida/scripts/chinese/" target="_blank">testing simplified Chinese and also traditional Chinese</a>. In this case the ISO 639-2 specification does not delineate between simplified Chinese and traditional Chinese or US English and British English.&#160; In this case we could “make up” a 3-letter designation such as GBR for Great Britain, or CHT for Chinese (traditional). </p>
<p>Or, perhaps a better solution would be to use the <a href="http://msdn.microsoft.com/en-us/library/0h88fahh%28v=VS.85%29.aspx" target="_blank">Locale Identifiers (LCID)</a> used by Windows to identify specific locales (rather than languages). The solution is identical to the above except instead of calling the ThreeLetterISOLanguageName property we call the LCID property as illustrated below. </p>
<p class="csharpcode">
<pre><span class="lnum">   1:  </span>            <span class="kwrd">string</span> testDataFileName = <span class="str">&quot;testdata.txt&quot;</span>;</pre>
<pre><span class="lnum">   2:  </span>&#160;</pre>
<pre><span class="lnum">   3:  </span>            CultureInfo ci = CultureInfo.CurrentCulture;</pre>
<pre><span class="lnum">   4:  </span>&#160;</pre>
<pre><span class="lnum">   5:  </span>            <span class="rem">// Path to server location where static files exist </span></pre>
<pre><span class="lnum">   6:  </span>            <span class="kwrd">string</span> path = Path.GetFullPath(</pre>
<pre><span class="lnum">   7:  </span>                Environment.GetFolderPath(Environment.SpecialFolder.Desktop));</pre>
<pre><span class="lnum">   8:  </span>&#160;</pre>
<pre><span class="lnum">   9:  </span>            <span class="rem">// Read file contents</span></pre>
<pre><span class="lnum">  10:  </span>            <span class="kwrd">using</span> (StreamReader readFile = </pre>
<pre><span class="lnum">  11:  </span>                <span class="kwrd">new</span> StreamReader(Path.Combine(</pre>
<pre><span class="lnum">  12:  </span>                    path, <span class="kwrd">string</span>.Concat(</pre>
<pre><span class="lnum">  13:  </span>                    ci.LCID, testDataFileName))))</pre>
<pre><span class="lnum">  14:  </span>            {</pre>
<pre><span class="lnum">  15:  </span>                <span class="rem">//parse test data and do test stuff</span></pre>
<pre><span class="lnum">  16:  </span>            }</pre>
</p>
<p>Of course, now we would need to name our static file names with the appropriate LCID decimal number such as</p>
<ul>
<li>1028testdata.txt would be traditional Chinese used in Taiwan, and</li>
<li>2052testdata.txt would be for simplified Chinese used in PRC</li>
</ul>
<p>Personally, I prefer getting the LCID as it provides greater control and more specificity. But the down side of using LCIDs is that if you may end up having multiple files that contain the same contents. For example, although Singapore, Malaysia, and PRC all use simplified Chinese there are 3 different LCIDs. </p>
<p>There are other properties that allow you to get the culture info for the current user in Windows, and the right property to use ultimately depends on your specific needs. But, CultureInfo class members can easily be used to manage localized static data files or even manage control flow through an automated test that has specific dependencies on a language or a locale setting.</p>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/WP_000019.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px; padding-top: 0px" title="WP_000019" border="0" alt="WP_000019" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/08/WP_000019_thumb.jpg" width="468" height="357" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/08/21/dealing-with-localelanguage-specific-static-test-data/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>The SDET vs STE Debate Redux: It&#8217;s only a title!</title>
		<link>http://www.testingmentor.com/imtesty/2011/07/22/the-sdet-vs-ste-debate-redux-its-only-a-title/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/07/22/the-sdet-vs-ste-debate-redux-its-only-a-title/#comments</comments>
		<pubDate>Fri, 22 Jul 2011 15:55:26 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[General Testing Topics]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/07/22/the-sdet-vs-ste-debate-redux-its-only-a-title/</guid>
		<description><![CDATA[Every few months the STE vs. SDET debate reemerges like the crazy outcast relative that comes to visit unexpectedly and sits around complaining about imaginative ailments, and reminiscing about how things were in the good ol’ days. We certainly don’t want to be rude to our relatives, so we tolerate their rants while watching the [...]]]></description>
			<content:encoded><![CDATA[<p>Every few months the STE vs. SDET debate reemerges like the crazy outcast relative that comes to visit unexpectedly and sits around complaining about imaginative ailments, and reminiscing about how things were in the good ol’ days. We certainly don’t want to be rude to our relatives, so we tolerate their rants while watching the clock and giving subtle suggestions about the late time. But, with the ridiculous ‘debate’ between STE and SDET I can be rude; drop it! It’s a baseless discussion without merit. It’s only a title!</p>
<p>In <a href="http://www.testingmentor.com/imtesty/2009/11/10/the-ste-vs-sdet-debate/" target="_blank">this previous post</a> I explained the business reasons why Microsoft changed the title from STE to SDET. But, for some reason people commonly mistake the title with the role or job function. In the good ol’ days our internal job description for STE at level 59 included ‘must be able to debug other’s code,’ and ‘design automated tests.’ Almost all STEs hired prior to 1995 had coding questions as part of their interview and were expected to grow their ‘technical skills’ throughout their career.&#160; That was the traditional role of the STE.</p>
<p>As I explained in <a href="http://www.testingmentor.com/imtesty/2009/11/10/the-ste-vs-sdet-debate/" target="_blank">this previous post</a> we established the title of SDET to ensure that testers at a given level in one organization in the company had comparable skills to another tester in a different organization. As part of the title change, the company decided that we needed to reestablish the base skill set of our testers to include ‘technical competence.’ Unfortunately when the career profiles were introduced some managers misinterpreted &#8216;technical competence&#8217; with raw coding skills and the naive ideology of 100% automation. These same managers now complain their SDETs don’t excel at ‘bug finding’ and customer advocacy. </p>
<p>On my current team, the program managers are big customer advocates. They run their own set of ‘scenarios’ against new builds at least weekly. My feature area is testing private APIs on our platform. Our primary customers are the developers who consume those APIs, but we also must understand how bugs we find via our automated tests might manifest themselves and impact our customers. So, our team spends quite a bit of time also self-hosting, doing exploratory testing, and we even started a new approach that takes customer scenarios to the n-th degree that we call &quot;day in the life&quot; testing to help us better understand how customers might use our product throughout their busy days. Our product has 93% customer satisfaction.</p>
<p>So, if its true that the SDETs on some teams aren’t finding bugs and lack customer focus (and I suspect it is for some teams) then they hired the wrong people onto their test team. If SDETs don&#8217;t balance their technical competence with customer empathy then we have a problem; and I will say it is likely a management problem.</p>
<p>The testing profession is diverse and requires people to perform different roles or job functions during the development process and over the course of their career. Microsoft didn’t eradicate the STE “role” we simply changed the title of the people we hire in our testing “roles” and reestablished the traditional expectations of people in that role.</p>
<p>Differentiating between STE and SDET in our industry seems nonsensical to me, and I also think this false differentiation ultimately limits our potential to positively impact our customer&#8217;s experience and advance the profession. Testers today face many challenges, and hiring great testers (regardless of the job title) is about finding people who not only have a passion and drive to help improve our customer&#8217;s experience and satisfaction, but can also solve tough technical challenges to advance the craft and help improve the company’s business.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/07/22/the-sdet-vs-ste-debate-redux-its-only-a-title/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Winning with team work</title>
		<link>http://www.testingmentor.com/imtesty/2011/07/12/winning-with-team-work/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/07/12/winning-with-team-work/#comments</comments>
		<pubDate>Wed, 13 Jul 2011 04:51:05 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[General Testing Topics]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/07/12/winning-with-team-work/</guid>
		<description><![CDATA[Last Sunday evening our summer league Monarchs hockey team had a game against the Ice Dogs. In our previous game we tied against this team so I knew this would not be an easy game. To compound things we had a short bench (10 players and a goalie); enough for 2 forward lines and 2 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/07/image.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 0px 0px 10px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" align="right" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/07/image_thumb.png" width="309" height="473" /></a>Last Sunday evening our summer league Monarchs hockey team had a game against the Ice Dogs. In our previous game we tied against this team so I knew this would not be an easy game. To compound things we had a short bench (10 players and a goalie); enough for 2 forward lines and 2 defense lines. It was a hard game and our team really congealed and we played one of our best games this summer season. Just like the saying ‘when the going gets tough the tough get going.’ When you have a great team of people they don’t sit around and cry like a bunch of panty wastes, play the victim card, point fingers, or incessantly complain. A good team buckles down in hard times in spite of the difficulties that might lie ahead and work together to get things done. Individual hero’s need not apply. Team’s don’t worship heroes; they value every person on the team.</p>
<p>The weekend hockey game was a good break for me. You see, we have been in ship mode for our Mango release on the Windows Phone. The hockey game was a good outlet for some pent up frustration. Every D-man had at least 2 shots on goal, and I blocked a couple of shots; one off the mask and one off my inner thigh of course where there are no pads, and yes it left a pretty good bruise. But, as they say, “pain is temporary; a win is forever.”</p>
<p>Seemingly against the odds, we ended up winning the hockey game 5 to 1.</p>
<p>Ship mode often times gets a little crazy. Second guessing takes on a whole new meaning. “Did you test this&quot;?” “What about that?” “I have a situation when I do such and such, and the sun came out (remember we’re in Seattle) something bad happened. Have you seen this before?” Some people run around looking for fires, others are trying to start them. </p>
<p>As I get older I have learned not to react to fires as I did in my younger days. I have learned that sometimes fires aren’t really fires at all; it’s just a spark that someone is recklessly trying to fan into a flame. Sometimes there are fires that burn themselves out, but you just have to manage them in a control burn. And then there are the fires that have to be dealt with. Dealing with fires late in the product cycle is not fun for on the team. But, a team of people are responsible for doing just that and it is seldom easy; and it happens in the ship room.</p>
<p>Our ship room looks at a lot of data every day throughout the product cycle to help us manage our release schedule and stay focused. In ship mode, data is scrutinized even closer and every bug goes under the microscope. Managers must now work together to make some hard decisions about whether to take a fix. There is often intense discussion, but you will never hear anyone play the consultant card saying “it depends.” These guys in ship room have been in the game a long time, they know the risks and they know the business. Of course they know “it depends.” They don’t want a bunch of hand waving and bloviating, they need facts to make hard decisions. If you say an issue going to adversely affect customers you better be able to explain how customers are impacted, how many customers are impacted, how customers get into that predicament, and know if there is a potential work-around.</p>
<p>In the end, a team of senior managers must make hard decisions about what issues to punt and which to fix based on the information that is presented. This never easy at anytime during the product cycle, but in ship mode each issue is carefully investigated down to root cause, the fix is understood, and the impact of fix and testing considerations are well defined before the final decision is made. Of course, many products are schedule driven, but at the forefront of every decision in our ship room is customer impact. Perhaps that is why customer satisfaction for Windows Phone 7 is at 93%, and why I am glad to be on a team that works hard to do the right thing for our customers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/07/12/winning-with-team-work/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Levels of Testing</title>
		<link>http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/#comments</comments>
		<pubDate>Sat, 25 Jun 2011 19:42:33 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[General Testing Topics]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/</guid>
		<description><![CDATA[The other day my friend and colleague Alan Page was asking about the levels of testing. When I teach fundamental testing concepts I often use the the ‘levels of testing’ as a model to help students conceptualize different types of testing activities and potentially different stages of testing processes during the software development lifecycle (SDLC). [...]]]></description>
			<content:encoded><![CDATA[<p>The other day my friend and colleague <a href="http://angryweasel.com/blog/" target="_blank">Alan Page</a> was asking about the levels of testing. When I teach fundamental testing concepts I often use the the ‘levels of testing’ as a model to help students conceptualize different types of testing activities and potentially different stages of testing processes during the software development lifecycle (SDLC). In his book “<a href="http://www.amazon.com/Software-Testing-Techniques-Boris-Beizer/dp/0442206720/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1309030587&amp;sr=1-1" target="_blank">Software Testing Techniques</a>,” B. Beizer writes there are 3 different types of testing activities. He groups them as unit/component, integration, and system ‘levels of testing.’ The different levels are explained by Beizer as:</p>
<blockquote><p>“A <strong>unit</strong> is the smallest testable piece of software, by which I mean it can be compiled or assembled, linked, loaded, and put under the control of a test harness or driver.”</p>
<p>“A <strong>component</strong> is an integrated aggregate of one or more units. A unit is a component a component with subroutines it calls is a component, etc. By this (recursive) definition, a component can be anything from a unit to an entire system.”</p>
<p>“<strong>Unit testing</strong> is the testing we do to show that the unit does not satisfy its functional specification and/or that its implemented structure does not match the intended design structure.”</p>
</blockquote>
<p>It’s interesting to note Beizer’s explanation of the purpose of unit testing. Perhaps this is the inspiration behind TDD concepts rediscovered 10 years later. It also is counter to the typical ideology that unit tests are intended to “prove it works.” </p>
<blockquote><p>“<strong>Integration</strong> is a process by which components are aggregated to create larger components.”</p>
<p>“<strong>Integration testing</strong> is testing done to show that even though the components were individually satisfactory, as demonstrated by successful passage of component tests, the combination of components are incorrect or inconsistent. “Integration testing is specifically aimed at exposing the problems that arise from the combination of components.” </p>
<p>“A <strong>system</strong> is a big component.”</p>
<p>“<strong>System testing</strong> is aimed at revealing bugs that cannot be attributed to components as such, to the inconsistencies between components, or to the planned interactions of components and other objects. “System testing concerns issues and behaviors that can only be exposed by testing the entire integrated system or a major part of it.”</p>
</blockquote>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/06/image.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="Levels of testing model" align="left" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/06/image_thumb.png" width="371" height="271" /></a>I often us the following visual model to illustrate the concept of ‘level of testing.’ This model is intended to show how each level builds upon the previous level, and also shows the increasing scope of each level of testing. </p>
<p>The problem with this model is that if we don’t understand the internal structure of software such as methods (or functions), classes, APIs or interfaces,&#160; forms or other UI elements, etc., it is difficult to differentiate between the different levels other than system (the entire integrated system, or product), and the other levels. </p>
<p>One way to explain this model is that unit testing level tests individual methods or functions in a class. Component level testing tests a public methods that call one or more methods in a class. Integration tests are targeted at testing individual APIs or combinations of APIs treating the APIs as black boxes but usually without traversing through a user interface. And system testing tests both functional aspects and behavioral aspects of the entire product’s components together in a single build.&#160; Obviously, the scope of testing at the system level of testing is very large and includes functional testing of computational logic, non-functional testing such as performance, security, etc, and behavioral testing such as usability, look and feel, etc.</p>
<p>Another problem with this model is that it may appear that by focusing testing efforts at the system level (e.g. testing each build through a user interface) we would have greater coverage and implicitly cover the ‘levels’ below the system. Unfortunately this is not always the case because </p>
<ul>
<li>the user interface can mask or hide some functional bugs in public APIs that are called by other developers </li>
<li>the system is large and complex and we may miss underlying functional bugs </li>
</ul>
<p>As Beizer indicated, each ‘level’ of testing has different objectives and can help identify certain types of bugs more efficiently as compared to the other ‘levels of testing.’ In our experience at Microsoft, we have learned that hiring massive numbers of people to test at the system level with little or no unit/component or integration testing does not necessarily result in higher “quality,” and may cost more in maintenance and support due to undetected functional issues. </p>
<p><a href="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/06/image1.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" align="right" src="http://www.testingmentor.com/imtesty/wp-content/uploads/2011/06/image_thumb1.png" width="400" height="351" /></a>Another model that I also like to show comes from <a href="http://www.amazon.com/s/ref=nb_sb_ss_i_0_59?url=search-alias%3Dstripbooks&amp;field-keywords=agile+testing+a+practical+guide+for+testers+and+agile+teams&amp;sprefix=agile+testing+a+practical+guide+for+testers+and+agile+teams&amp;x=0&amp;y=0" target="_blank">Agile Testing: A Practical Guide for Testers and Agile Teams</a> by Lisa Crispin and Janet Gregory. In their chapter on automation they illustrate the “test automation pyramid” with 3 layers of automated tests.</p>
<p>This model also shows how automated tests benefit from the underlying automation, and each layer is targeted towards helping the tester (and developer) identify different types of issues during the SDLC. I also like this example because it shows where automated tests are most effective or provide the greatest value to developers and testers. </p>
<p>Ideally there should be heavy emphasis on unit/component level testing, additional investment in API level testing, and as Crispin and Gregory state, “The top tier represents what should be the smallest automation effort, because the tests generally provide the lowest ROI.” While ROI means different things to different people, I would generally agree that GUI automated tests tend to be less reliable and require much more maintenance as compared to other levels of automation.</p>
<p>There are also some illustrations of testing levels that show a sequential progression from unit testing to component testing to integration testing and finally to system testing. In my opinion these don’t provide much value to anyone other than process wonks who are only capable of linear thought. There are also other models of ‘levels of testing’ that folks have devised they use usually to separate unique classes of system testing. Models are abstract concepts that can be used to help explain complex systems, and sometimes a little more detail in the model helps people understand the system.</p>
<p>But, I guess that is both the benefit and the drawback of models. Models can help explain complex concepts, but they can also be misused when single-minded individuals attempt to create rigid linear processes from an abstract model, or assume how someone actually implements a model is representative of the abstract concepts that someone attempted to present in a model.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/06/25/levels-of-testing/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Test Automation: Beyond Rudimentary Script-lets</title>
		<link>http://www.testingmentor.com/imtesty/2011/05/19/test-automation-beyond-rudimentary-script-lets/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/05/19/test-automation-beyond-rudimentary-script-lets/#comments</comments>
		<pubDate>Thu, 19 May 2011 09:54:07 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[Test Automation]]></category>
		<category><![CDATA[Random Test Data Generation]]></category>
		<category><![CDATA[Test Case Design]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/05/19/test-automation-beyond-rudimentary-script-lets/</guid>
		<description><![CDATA[When I wake up in the morning I like to go out onto my back deck, watch critters scramble back into the wooded area behind my house, feed the koi, and sit down by the pond and enjoy a cup of coffee. I use this time to gather my thoughts about things I want to [...]]]></description>
			<content:encoded><![CDATA[<p>When I wake up in the morning I like to go out onto my back deck, watch critters scramble back into the wooded area behind my house, feed the koi, and sit down by the pond and enjoy a cup of coffee. I use this time to gather my thoughts about things I want to accomplish that day and run through a mental list of meetings for that day. Sometimes I just think about cutting the grass or weeding the gardens. This morning morning I awoke to a rather cold 36° F with frost on the ground. Who would have thought frost in mid-May? This morning as I drank my coffee I thought it was a good thing that I procrastinated and haven’t moved my plants from the greenhouse into the gardens yet. But, despite the morning chill it is turning out to be a beautiful day here in the PNW.</p>
<p>Last week on my way to speak at TestNet in the Netherlands I <a href="http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/" target="_blank">posted about “bad automation</a>.” More to the point, it was about how we sometimes cobble UI automated tests that attempt to mimic some rudimentary set of sequential actions that we think a customer might execute and pass it off as an automated functional test. Some of these types of “automated tests” might make sense in limited situations, perhaps similar to how unit tests give us a very basic level of confidence after code churn. But, I think one of the reasons that test automation generally gets a bad rap is because of overly simplistic scripts that mindlessly perform the same thing over and over again and provides little real value to the team beyond emotional ‘feel-good’ or ‘frustration’ that often accompanies automation projects. So, building upon the example from last week, let’s look at how we might consider a few simple design principles and craft a more robust functional automated test that increases test coverage, helps reduce overall risk, and may potentially expose unexpected errors.</p>
<h5><strong>The purpose of the test<br />
</strong><strong><strong>Think about abstract big picture rather than a myopic singular purpose</strong></strong></h5>
<p>In the crud example we basically entered a static “hard-coded” string into a textbox and used a simplistic oracle to make sure the “test data” appeared in a message box. To me, this is about the same as retesting 1 + 1 = 2 over and over again to verify that a calculator program’s addition functionality is computationally correct. IMHO, a “functional test” that verifies a hard-coded string gives great confidence that that string works, but not other strings.</p>
<p>The purpose of the this test is not to just to verify “Boob” displays in the message box. Even if this were a regression test we could manually test the word “Boob” if necessary, but in the bigger picture the purpose is to provide confidence that <strong>any</strong> Unicode string (with a max length of 25 characters) is rendered correctly in the text of the message box. Of course, we can’t test every possible permutation of Unicode characters in strings with varying lengths between 0 and 25 characters. But, we can design a functional test that passes in multiple Unicode string variant to increase overall test coverage and still achieve our specific purpose without hard-coded values.</p>
<p>As Gaurav Pandey suggested in the comments of the <a href="http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/" target="_blank">previous post</a> one way to increase test data through put is to use a data-driven automation approach. Data-driven automation is certainly one way to increase the breadth of test data in an automated test especially if the test data contains historical failure indicators (test data that revealed problems in the past in this given context), and ‘customer-like’ data. The approach I took in this example is to use a random string generator (<a href="http://www.testingmentor.com/automation/sdk/babel/babel.htm" target="_blank">Babel</a>). Each time we iterate through the for loop line 66 calls the GetRandomTestDataVariant() method to generate a new random Unicode string using the Babel library. This random string variable is also used as the oracle for this test.</p>
<h5><span style="font-weight: bold;">Automation perspective<br />
</span><span style="font-weight: bold;">Think about programming the computer rather than trying to program human interactions </span></h5>
<p>In the previous example we used Sendkeys method to navigate the user interface using key mnemonics, enter text into the text box and manipulate the button controls. That approach to automating the user interface is often unreliable because Sendkeys send the message to whichever window has focus. Another problem with this approach is that key mnemonics could change leading to additional maintenance costs, and test failures on localized versions of the software where key mnemonics are different from the English language version.</p>
<p>In this example, instead of using Sendkeys methods we used UI Automation to get the automation elements to access to user interface elements on the desktop programmatically. This can also be accomplished by p-invoking various native functions in the user32.dll library. For example, the NativeMethod class in line 21 p-invokes the GetForegroundWindow() function and we use this in line 227 of our oracle to get the window handle of the message box. Line 51 gets the automation element objects on the main form that we will use to enter text into the name textbox and emulate clicking the button controls.</p>
<p>But sometimes we can accomplish tasks in a UI automated test without manipulating UI elements programmatically. For example, suppose we want to change our user locale settings during a test. From the customer perspective we would launch the Regional and Language control panel applet in the control panel and change a desired settings on the appropriate tab. However, in a <a href="http://www.testingmentor.com/imtesty/2010/06/03/globalization-testing-basic-international-sufficiency/" target="_blank">previous post last year</a> I explained how we could programmatically manipulate user locale settings using the <a href="http://www.testingmentor.com/automation/sdk/globaltester/GlobalTester.htm" target="_blank">GlobalTester library</a> without trying to manipulate UI elements.</p>
<p>Just about any interaction a user can do via the user interface we can programmatically emulate or manipulate the system below the UI via Windows APIs. The point here is that when we design our automated tests we should harness the power of the computer and think about what the computer is capable of doing, and not limit our automated test design to a set of simplistic sequential steps that an end user might perform in a scripted end-to-end scenario.</p>
<h5><span style="font-weight: bold;">Predict programmatic unpredictability<br />
</span><span style="font-weight: bold;">Think about what can go wrong and try to deal with it gracefully</span></h5>
<p>Poorly designed UI automation fails exactly 2.5 seconds after someone stops watching it run. When we launch an application we can visually ‘see’ when it is ‘ready’ to interact with. If an application launches in .25 seconds on one machine, but then takes 1 second on a different machine the test might fail due to a race condition in the automated test. Synchronization issues between the automated test and the application under test are a frequent problem with UI automation.</p>
<p>Polling loops are one way to help synchronize the automated test with the application under test to ensure that automation elements or windows are in the proper state before executing the next statement in the program. For example, the polling loop in the LaunchApplication() method starting at line 115 ‘looks’ for the appropriately ‘named’ form on the windows desktop for up to 5 seconds. Another polling loop in the oracle on line 225 looks for the Message box window to ensure it is instantiated before trying to get the the message box text.</p>
<p><a href="http://testingmentor.com/imtesty/wp-content/uploads/2011/05/image1.png"><img style="background-image: none; margin: 0px 0px 0px 5px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border-width: 0px;" title="image" src="http://testingmentor.com/imtesty/wp-content/uploads/2011/05/image_thumb1.png" border="0" alt="image" width="303" height="595" align="right" /></a>Of course some people might argue that the example code in the previous post is more simple, and is only about 50 lines of code or so. But, of course, short and simple often only provides simplistic value. Many test consultant now agree that testers require some degree of proficiency in a programming language in order to craft well-designed automated tests. Record/playback script-lets and rudimentary scripted tests can provide some value in limited situations. But, more robust automation can help reduce overall costs, improve confidence by efficiently increasing test coverage, and can even expose unexpected anomalies that other approaches may or may not discover.</p>
<p>The image to the right illustrates 25 randomly generated strings in one test pass. Although this particular automated test does not use ‘real world’ data, it significantly increases the use of Unicode characters across the spectrum of possibilities, and provides greater confidence that any Unicode character or combination of characters would not cause an error. Also, it provides greater breadth of coverage of test data more efficiently (time &amp; effort) as compared to manual testing.</p>
<p>Below exemplifies some concepts discussed above, and is one possible solution (that can be improved…such as the logging). The wonderful thing about coding automated tests is that it can provide another perspective that creative testers can use to help them craft tests that can add value to the project.</p>
<p>&nbsp;</p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span><span class="kwrd">namespace</span> HelloTest</pre>
<pre><span class="lnum">   2:  </span>{</pre>
<pre><span class="lnum">   3:  </span>  <span class="kwrd">using</span> System;</pre>
<pre><span class="lnum">   4:  </span>  <span class="kwrd">using</span> System.Diagnostics;</pre>
<pre><span class="lnum">   5:  </span>  <span class="kwrd">using</span> System.IO;</pre>
<pre><span class="lnum">   6:  </span>  <span class="kwrd">using</span> System.Runtime.InteropServices;</pre>
<pre><span class="lnum">   7:  </span>  <span class="kwrd">using</span> System.Windows.Automation;</pre>
<pre><span class="lnum">   8:  </span>  <span class="kwrd">using</span> TestingMentor.TestTool.Babel;</pre>
<pre><span class="lnum">   9:  </span></pre>
<pre><span class="lnum">  10:  </span>  <span class="kwrd">internal</span> <span class="kwrd">class</span> Constant</pre>
<pre><span class="lnum">  11:  </span>  {</pre>
<pre><span class="lnum">  12:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">string</span> AutName = <span class="str">"hello.exe"</span>;</pre>
<pre><span class="lnum">  13:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">string</span> AutFormTitle = <span class="str">"HelloForm"</span>;</pre>
<pre><span class="lnum">  14:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">string</span> AutNameTextBox = <span class="str">"textboxFirstName"</span>;</pre>
<pre><span class="lnum">  15:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">string</span> AutOKButton = <span class="str">"buttonOK"</span>;</pre>
<pre><span class="lnum">  16:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">string</span> AutClearButton = <span class="str">"buttonClear"</span>;</pre>
<pre><span class="lnum">  17:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">int</span> FirstNameTextboxLength = 25;</pre>
<pre><span class="lnum">  18:  </span>    <span class="kwrd">internal</span> <span class="kwrd">const</span> <span class="kwrd">int</span> MaxPollCount = 50;</pre>
<pre><span class="lnum">  19:  </span>  }</pre>
<pre><span class="lnum">  20:  </span></pre>
<pre><span class="lnum">  21:  </span>  <span class="kwrd">public</span> <span class="kwrd">class</span> NativeMethod</pre>
<pre><span class="lnum">  22:  </span>  {</pre>
<pre><span class="lnum">  23:  </span>    [DllImport(<span class="str">"user32.dll"</span>)]</pre>
<pre><span class="lnum">  24:  </span>    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">extern</span> IntPtr GetForegroundWindow();</pre>
<pre><span class="lnum">  25:  </span>  }</pre>
<pre><span class="lnum">  26:  </span></pre>
<pre><span class="lnum">  27:  </span>  <span class="kwrd">class</span> SampleHelloTest</pre>
<pre><span class="lnum">  28:  </span>  {</pre>
<pre><span class="lnum">  29:  </span>    <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)</pre>
<pre><span class="lnum">  30:  </span>    {</pre>
<pre><span class="lnum">  31:  </span>      <span class="kwrd">try</span></pre>
<pre><span class="lnum">  32:  </span>      {</pre>
<pre><span class="lnum">  33:  </span>        Stopwatch timer = <span class="kwrd">new</span> Stopwatch();</pre>
<pre><span class="lnum">  34:  </span>        timer.Start();</pre>
<pre><span class="lnum">  35:  </span></pre>
<pre><span class="lnum">  36:  </span>        <span class="kwrd">int</span> testIterationCount = 25;</pre>
<pre><span class="lnum">  37:  </span></pre>
<pre><span class="lnum">  38:  </span>        <span class="rem">// Declare a new process and automation elements</span></pre>
<pre><span class="lnum">  39:  </span>        Process autProcess = <span class="kwrd">new</span> Process();</pre>
<pre><span class="lnum">  40:  </span>        AutomationElement desktopPath = AutomationElement.RootElement;</pre>
<pre><span class="lnum">  41:  </span>        AutomationElement autForm = <span class="kwrd">null</span>;</pre>
<pre><span class="lnum">  42:  </span>        AutomationElement nameTextbox;</pre>
<pre><span class="lnum">  43:  </span>        AutomationElement clearButton;</pre>
<pre><span class="lnum">  44:  </span>        AutomationElement okButton;</pre>
<pre><span class="lnum">  45:  </span></pre>
<pre><span class="lnum">  46:  </span>        <span class="rem">// Launch the AUT</span></pre>
<pre><span class="lnum">  47:  </span>        autProcess = LaunchApplication(</pre>
<pre><span class="lnum">  48:  </span>          Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),</pre>
<pre><span class="lnum">  49:  </span>          Constant.AutName),</pre>
<pre><span class="lnum">  50:  </span>          desktopPath,</pre>
<pre><span class="lnum">  51:  </span>          Constant.MaxPollCount,</pre>
<pre><span class="lnum">  52:  </span>          <span class="kwrd">ref</span> autForm);</pre>
<pre><span class="lnum">  53:  </span></pre>
<pre><span class="lnum">  54:  </span>        <span class="rem">// Get the UI automation element object on main form</span></pre>
<pre><span class="lnum">  55:  </span>        GetUIAutomationElements(autForm, <span class="kwrd">out</span> nameTextbox, <span class="kwrd">out</span> okButton, <span class="kwrd">out</span> clearButton);</pre>
<pre><span class="lnum">  56:  </span></pre>
<pre><span class="lnum">  57:  </span>        <span class="rem">// Verify the textbox control is 'visible'</span></pre>
<pre><span class="lnum">  58:  </span>        <span class="kwrd">if</span> (nameTextbox != <span class="kwrd">null</span> &amp;&amp;</pre>
<pre><span class="lnum">  59:  </span>          (<span class="kwrd">bool</span>)nameTextbox.GetCurrentPropertyValue(AutomationElement.IsEnabledProperty))</pre>
<pre><span class="lnum">  60:  </span>        {</pre>
<pre><span class="lnum">  61:  </span>          <span class="rem">// iterate through a specified number of test data variants</span></pre>
<pre><span class="lnum">  62:  </span>          <span class="kwrd">for</span> (<span class="kwrd">int</span> testCount = 0; testCount &lt; testIterationCount; testCount++)</pre>
<pre><span class="lnum">  63:  </span>          {</pre>
<pre><span class="lnum">  64:  </span></pre>
<pre><span class="lnum">  65:  </span>            <span class="kwrd">int</span> seedValue;</pre>
<pre><span class="lnum">  66:  </span>            <span class="kwrd">string</span> testData = GetRandomTestDataVariant(<span class="kwrd">out</span> seedValue);</pre>
<pre><span class="lnum">  67:  </span></pre>
<pre><span class="lnum">  68:  </span>            <span class="rem">// Put test string into textbox</span></pre>
<pre><span class="lnum">  69:  </span>            SetTextboxText(nameTextbox, testData);</pre>
<pre><span class="lnum">  70:  </span></pre>
<pre><span class="lnum">  71:  </span>            <span class="rem">// Emulate clicking the OK button to instantiate the message box</span></pre>
<pre><span class="lnum">  72:  </span>            PushButton(okButton);</pre>
<pre><span class="lnum">  73:  </span></pre>
<pre><span class="lnum">  74:  </span>            <span class="rem">// Oracle to compare actual vs expected result and log results/important information</span></pre>
<pre><span class="lnum">  75:  </span>            <span class="kwrd">string</span> actualResult = <span class="kwrd">string</span>.Empty;</pre>
<pre><span class="lnum">  76:  </span>            <span class="kwrd">if</span> (ValidateMessageBoxStringOracle(testData, Constant.MaxPollCount, <span class="kwrd">out</span> actualResult))</pre>
<pre><span class="lnum">  77:  </span>            {</pre>
<pre><span class="lnum">  78:  </span>              <span class="rem">// Log Test Results as appropriate </span></pre>
<pre><span class="lnum">  79:  </span>              Console.WriteLine(testData);</pre>
<pre><span class="lnum">  80:  </span>            }</pre>
<pre><span class="lnum">  81:  </span>            <span class="kwrd">else</span></pre>
<pre><span class="lnum">  82:  </span>            {</pre>
<pre><span class="lnum">  83:  </span>              Console.BackgroundColor = ConsoleColor.Red;</pre>
<pre><span class="lnum">  84:  </span>              Console.WriteLine(</pre>
<pre><span class="lnum">  85:  </span>                <span class="str">"FAIL: Expected Result = {0}, Actual Result = {1}, Seed Value = {2}"</span>,</pre>
<pre><span class="lnum">  86:  </span>                testData, actualResult, seedValue);</pre>
<pre><span class="lnum">  87:  </span>            }</pre>
<pre><span class="lnum">  88:  </span></pre>
<pre><span class="lnum">  89:  </span>            <span class="rem">// Emulate clicking the clear button</span></pre>
<pre><span class="lnum">  90:  </span>            PushButton(clearButton);</pre>
<pre><span class="lnum">  91:  </span>          }</pre>
<pre><span class="lnum">  92:  </span>        }</pre>
<pre><span class="lnum">  93:  </span>        <span class="kwrd">else</span></pre>
<pre><span class="lnum">  94:  </span>        {</pre>
<pre><span class="lnum">  95:  </span>          Console.WriteLine(<span class="str">"TEST FAILURE: Textbox control not found"</span>);</pre>
<pre><span class="lnum">  96:  </span>        }</pre>
<pre><span class="lnum">  97:  </span></pre>
<pre><span class="lnum">  98:  </span>        <span class="rem">// Clean up the test environment </span></pre>
<pre><span class="lnum">  99:  </span>        CloseAutProcess(autProcess);</pre>
<pre><span class="lnum"> 100:  </span></pre>
<pre><span class="lnum"> 101:  </span>        <span class="rem">// Log total elapsed time and any additional important info</span></pre>
<pre><span class="lnum"> 102:  </span>        timer.Stop();</pre>
<pre><span class="lnum"> 103:  </span>        Console.WriteLine(<span class="str">"Total elapsed time: {0} seconds"</span>, timer.Elapsed.TotalSeconds);</pre>
<pre><span class="lnum"> 104:  </span>      }</pre>
<pre><span class="lnum"> 105:  </span>      <span class="kwrd">catch</span> (Exception e)</pre>
<pre><span class="lnum"> 106:  </span>      {</pre>
<pre><span class="lnum"> 107:  </span>        Console.WriteLine(e.ToString());</pre>
<pre><span class="lnum"> 108:  </span>      }</pre>
<pre><span class="lnum"> 109:  </span>    }</pre>
<pre><span class="lnum"> 110:  </span></pre>
<pre><span class="lnum"> 111:  </span>    <span class="preproc">#region</span> HELPER METHODS</pre>
<pre><span class="lnum"> 112:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 113:  </span>    <span class="rem">/// Launches AUT process; or throws exception if process fails to launch</span></pre>
<pre><span class="lnum"> 114:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 115:  </span>    <span class="kwrd">internal</span> <span class="kwrd">static</span> Process LaunchApplication(</pre>
<pre><span class="lnum"> 116:  </span>      <span class="kwrd">string</span> autPathAndFilename,</pre>
<pre><span class="lnum"> 117:  </span>      AutomationElement desktop,</pre>
<pre><span class="lnum"> 118:  </span>      <span class="kwrd">int</span> maxPollCount,</pre>
<pre><span class="lnum"> 119:  </span>      <span class="kwrd">ref</span> AutomationElement myAutForm)</pre>
<pre><span class="lnum"> 120:  </span>    {</pre>
<pre><span class="lnum"> 121:  </span>      Process myProc = <span class="kwrd">new</span> Process();</pre>
<pre><span class="lnum"> 122:  </span>      myProc.StartInfo.FileName = autPathAndFilename;</pre>
<pre><span class="lnum"> 123:  </span>      <span class="kwrd">if</span> (myProc.Start())</pre>
<pre><span class="lnum"> 124:  </span>      {</pre>
<pre><span class="lnum"> 125:  </span>        <span class="rem">// Find the MyFontForm window</span></pre>
<pre><span class="lnum"> 126:  </span>        <span class="kwrd">int</span> pollCount = 0;</pre>
<pre><span class="lnum"> 127:  </span>        <span class="kwrd">do</span></pre>
<pre><span class="lnum"> 128:  </span>        {</pre>
<pre><span class="lnum"> 129:  </span>          myAutForm = desktop.FindFirst(</pre>
<pre><span class="lnum"> 130:  </span>            TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 131:  </span>              AutomationElement.AutomationIdProperty, Constant.AutFormTitle));</pre>
<pre><span class="lnum"> 132:  </span>          pollCount++;</pre>
<pre><span class="lnum"> 133:  </span>          System.Threading.Thread.Sleep(100);</pre>
<pre><span class="lnum"> 134:  </span>        }</pre>
<pre><span class="lnum"> 135:  </span>        <span class="kwrd">while</span> (myAutForm == <span class="kwrd">null</span> &amp;&amp; pollCount &lt; maxPollCount);</pre>
<pre><span class="lnum"> 136:  </span></pre>
<pre><span class="lnum"> 137:  </span>        <span class="kwrd">if</span> (myAutForm == <span class="kwrd">null</span>)</pre>
<pre><span class="lnum"> 138:  </span>        {</pre>
<pre><span class="lnum"> 139:  </span>          <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"Failed to find AUT form"</span>);</pre>
<pre><span class="lnum"> 140:  </span>        }</pre>
<pre><span class="lnum"> 141:  </span>      }</pre>
<pre><span class="lnum"> 142:  </span>      <span class="kwrd">return</span> myProc;</pre>
<pre><span class="lnum"> 143:  </span>    }</pre>
<pre><span class="lnum"> 144:  </span></pre>
<pre><span class="lnum"> 145:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 146:  </span>    <span class="rem">/// Get UI automation element objects on main form by property name</span></pre>
<pre><span class="lnum"> 147:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 148:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> GetUIAutomationElements(</pre>
<pre><span class="lnum"> 149:  </span>      AutomationElement autFormName,</pre>
<pre><span class="lnum"> 150:  </span>      <span class="kwrd">out</span> AutomationElement nameTextbox,</pre>
<pre><span class="lnum"> 151:  </span>      <span class="kwrd">out</span> AutomationElement okButton,</pre>
<pre><span class="lnum"> 152:  </span>      <span class="kwrd">out</span> AutomationElement clearButton)</pre>
<pre><span class="lnum"> 153:  </span>    {</pre>
<pre><span class="lnum"> 154:  </span>      nameTextbox = autFormName.FindFirst(</pre>
<pre><span class="lnum"> 155:  </span>        TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 156:  </span>          AutomationElement.AutomationIdProperty, Constant.AutNameTextBox));</pre>
<pre><span class="lnum"> 157:  </span>      okButton = autFormName.FindFirst(</pre>
<pre><span class="lnum"> 158:  </span>        TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 159:  </span>          AutomationElement.AutomationIdProperty, Constant.AutOKButton));</pre>
<pre><span class="lnum"> 160:  </span>      clearButton = autFormName.FindFirst(</pre>
<pre><span class="lnum"> 161:  </span>        TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 162:  </span>          AutomationElement.AutomationIdProperty, Constant.AutClearButton));</pre>
<pre><span class="lnum"> 163:  </span>    }</pre>
<pre><span class="lnum"> 164:  </span></pre>
<pre><span class="lnum"> 165:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 166:  </span>    <span class="rem">/// Generate a pseudo-random Unicode string using the Babel.dll automation library</span></pre>
<pre><span class="lnum"> 167:  </span>    <span class="rem">/// See http://www.testingmentor.com/automation/sdk/babel/babel.htm</span></pre>
<pre><span class="lnum"> 168:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 169:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">string</span> GetRandomTestDataVariant(<span class="kwrd">out</span> <span class="kwrd">int</span> seedValue)</pre>
<pre><span class="lnum"> 170:  </span>    {</pre>
<pre><span class="lnum"> 171:  </span>      <span class="kwrd">string</span> randomString = <span class="kwrd">string</span>.Empty;</pre>
<pre><span class="lnum"> 172:  </span>      <span class="kwrd">do</span></pre>
<pre><span class="lnum"> 173:  </span>      {</pre>
<pre><span class="lnum"> 174:  </span>        Random prng = <span class="kwrd">new</span> Random();</pre>
<pre><span class="lnum"> 175:  </span>        seedValue = prng.Next();</pre>
<pre><span class="lnum"> 176:  </span>        StringGenerator sg = <span class="kwrd">new</span> StringGenerator();</pre>
<pre><span class="lnum"> 177:  </span>        sg.Info.Seed = seedValue;</pre>
<pre><span class="lnum"> 178:  </span>        sg.Info.MaximumCharacterCount = Constant.FirstNameTextboxLength;</pre>
<pre><span class="lnum"> 179:  </span>        sg.Info.RandomizeCharacterCount = <span class="kwrd">true</span>;</pre>
<pre><span class="lnum"> 180:  </span>        sg.Info.AllowSurrogatePairCharacters = <span class="kwrd">false</span>;</pre>
<pre><span class="lnum"> 181:  </span>        randomString = sg.Polyglot();</pre>
<pre><span class="lnum"> 182:  </span>      }</pre>
<pre><span class="lnum"> 183:  </span>      <span class="kwrd">while</span> (randomString.Contains(<span class="str">"\0"</span>));</pre>
<pre><span class="lnum"> 184:  </span></pre>
<pre><span class="lnum"> 185:  </span>      <span class="kwrd">return</span> randomString;</pre>
<pre><span class="lnum"> 186:  </span>    }</pre>
<pre><span class="lnum"> 187:  </span></pre>
<pre><span class="lnum"> 188:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 189:  </span>    <span class="rem">/// Generic method that enters the test data into the a specified textbox</span></pre>
<pre><span class="lnum"> 190:  </span>    <span class="rem">/// Throws exception if the length of the testData argument exceeds the size</span></pre>
<pre><span class="lnum"> 191:  </span>    <span class="rem">/// property of the textbox control, or textbox control in not enabled</span></pre>
<pre><span class="lnum"> 192:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 193:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> SetTextboxText(AutomationElement inputTextbox, <span class="kwrd">string</span> testData)</pre>
<pre><span class="lnum"> 194:  </span>    {</pre>
<pre><span class="lnum"> 195:  </span>      <span class="kwrd">try</span></pre>
<pre><span class="lnum"> 196:  </span>      {</pre>
<pre><span class="lnum"> 197:  </span>        ValuePattern input =</pre>
<pre><span class="lnum"> 198:  </span>          (ValuePattern)inputTextbox.GetCurrentPattern(ValuePattern.Pattern);</pre>
<pre><span class="lnum"> 199:  </span>        input.SetValue(testData);</pre>
<pre><span class="lnum"> 200:  </span>      }</pre>
<pre><span class="lnum"> 201:  </span>      <span class="kwrd">catch</span> (ElementNotEnabledException)</pre>
<pre><span class="lnum"> 202:  </span>      {</pre>
<pre><span class="lnum"> 203:  </span>        <span class="kwrd">throw</span> <span class="kwrd">new</span> ElementNotEnabledException(<span class="str">"Textbox is not enabled."</span>);</pre>
<pre><span class="lnum"> 204:  </span>      }</pre>
<pre><span class="lnum"> 205:  </span>      <span class="kwrd">catch</span> (InvalidOperationException)</pre>
<pre><span class="lnum"> 206:  </span>      {</pre>
<pre><span class="lnum"> 207:  </span>        <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Test data is invalid or exceeds maximum length."</span>);</pre>
<pre><span class="lnum"> 208:  </span>      }</pre>
<pre><span class="lnum"> 209:  </span>    }</pre>
<pre><span class="lnum"> 210:  </span></pre>
<pre><span class="lnum"> 211:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 212:  </span>    <span class="rem">/// Oracle that compares the randomly generated string against the variable</span></pre>
<pre><span class="lnum"> 213:  </span>    <span class="rem">/// result in the message box text</span></pre>
<pre><span class="lnum"> 214:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 215:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">bool</span> ValidateMessageBoxStringOracle(</pre>
<pre><span class="lnum"> 216:  </span>      <span class="kwrd">string</span> testData, <span class="kwrd">int</span> maxPollCount, <span class="kwrd">out</span> <span class="kwrd">string</span> actualResult)</pre>
<pre><span class="lnum"> 217:  </span>    {</pre>
<pre><span class="lnum"> 218:  </span>        <span class="kwrd">bool</span> result = <span class="kwrd">false</span>;</pre>
<pre><span class="lnum"> 219:  </span>        IntPtr msgBoxHandle = IntPtr.Zero;</pre>
<pre><span class="lnum"> 220:  </span>        AutomationElement messageBox;</pre>
<pre><span class="lnum"> 221:  </span>        <span class="kwrd">int</span> pollCount = 0;</pre>
<pre><span class="lnum"> 222:  </span>        actualResult = <span class="kwrd">string</span>.Empty;</pre>
<pre><span class="lnum"> 223:  </span>        <span class="kwrd">try</span></pre>
<pre><span class="lnum"> 224:  </span>        {</pre>
<pre><span class="lnum"> 225:  </span>          <span class="kwrd">do</span></pre>
<pre><span class="lnum"> 226:  </span>          {</pre>
<pre><span class="lnum"> 227:  </span>            msgBoxHandle = NativeMethod.GetForegroundWindow();</pre>
<pre><span class="lnum"> 228:  </span>            messageBox = AutomationElement.FromHandle(msgBoxHandle);</pre>
<pre><span class="lnum"> 229:  </span>            <span class="kwrd">string</span> className =</pre>
<pre><span class="lnum"> 230:  </span>              (<span class="kwrd">string</span>)messageBox.GetCurrentPropertyValue(AutomationElement.ClassNameProperty);</pre>
<pre><span class="lnum"> 231:  </span>            System.Threading.Thread.Sleep(100);</pre>
<pre><span class="lnum"> 232:  </span>            pollCount++;</pre>
<pre><span class="lnum"> 233:  </span>          }</pre>
<pre><span class="lnum"> 234:  </span>          <span class="kwrd">while</span> (msgBoxHandle == IntPtr.Zero &amp;&amp; pollCount &lt; maxPollCount);</pre>
<pre><span class="lnum"> 235:  </span></pre>
<pre><span class="lnum"> 236:  </span>          <span class="kwrd">if</span> (messageBox != <span class="kwrd">null</span>)</pre>
<pre><span class="lnum"> 237:  </span>          {</pre>
<pre><span class="lnum"> 238:  </span>            AutomationElement textbox = messageBox.FindFirst(</pre>
<pre><span class="lnum"> 239:  </span>              TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 240:  </span>                AutomationElement.AutomationIdProperty, <span class="str">"65535"</span>));</pre>
<pre><span class="lnum"> 241:  </span>            <span class="kwrd">string</span> messageBoxText =</pre>
<pre><span class="lnum"> 242:  </span>              (<span class="kwrd">string</span>)textbox.GetCurrentPropertyValue(AutomationElement.NameProperty);</pre>
<pre><span class="lnum"> 243:  </span>            actualResult =</pre>
<pre><span class="lnum"> 244:  </span>              messageBoxText.Substring(messageBoxText.IndexOf(<span class="str">'\u0020'</span>) + 1, testData.Length);</pre>
<pre><span class="lnum"> 245:  </span></pre>
<pre><span class="lnum"> 246:  </span>            <span class="kwrd">if</span> (actualResult.Equals(testData))</pre>
<pre><span class="lnum"> 247:  </span>            {</pre>
<pre><span class="lnum"> 248:  </span>              result = <span class="kwrd">true</span>;</pre>
<pre><span class="lnum"> 249:  </span>            }</pre>
<pre><span class="lnum"> 250:  </span></pre>
<pre><span class="lnum"> 251:  </span>            AutomationElement messageboxOKButton = messageBox.FindFirst(</pre>
<pre><span class="lnum"> 252:  </span>              TreeScope.Descendants, <span class="kwrd">new</span> PropertyCondition(</pre>
<pre><span class="lnum"> 253:  </span>                AutomationElement.ClassNameProperty, <span class="str">"Button"</span>));</pre>
<pre><span class="lnum"> 254:  </span>            PushButton(messageboxOKButton);</pre>
<pre><span class="lnum"> 255:  </span>          }</pre>
<pre><span class="lnum"> 256:  </span>          <span class="kwrd">else</span></pre>
<pre><span class="lnum"> 257:  </span>          {</pre>
<pre><span class="lnum"> 258:  </span>            actualResult = <span class="str">"Messagebox not found."</span>;</pre>
<pre><span class="lnum"> 259:  </span>          }</pre>
<pre><span class="lnum"> 260:  </span></pre>
<pre><span class="lnum"> 261:  </span>          <span class="kwrd">return</span> result;</pre>
<pre><span class="lnum"> 262:  </span>        }</pre>
<pre><span class="lnum"> 263:  </span>        <span class="kwrd">catch</span> (ArgumentOutOfRangeException)</pre>
<pre><span class="lnum"> 264:  </span>        {</pre>
<pre><span class="lnum"> 265:  </span>          <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentOutOfRangeException(<span class="str">"POSSIBLE TEST DATA FAILURE"</span>);</pre>
<pre><span class="lnum"> 266:  </span>        }</pre>
<pre><span class="lnum"> 267:  </span>        <span class="kwrd">catch</span> (NullReferenceException)</pre>
<pre><span class="lnum"> 268:  </span>        {</pre>
<pre><span class="lnum"> 269:  </span>          <span class="kwrd">throw</span> <span class="kwrd">new</span> NullReferenceException(<span class="str">"Messagebox unexpectedly became null"</span>);</pre>
<pre><span class="lnum"> 270:  </span>        }</pre>
<pre><span class="lnum"> 271:  </span>    }</pre>
<pre><span class="lnum"> 272:  </span></pre>
<pre><span class="lnum"> 273:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 274:  </span>    <span class="rem">/// Generic method to push a button control</span></pre>
<pre><span class="lnum"> 275:  </span>    <span class="rem">/// Throws exception if button control is not enabled, or buttonName is null</span></pre>
<pre><span class="lnum"> 276:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 277:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> PushButton(AutomationElement buttonName)</pre>
<pre><span class="lnum"> 278:  </span>    {</pre>
<pre><span class="lnum"> 279:  </span>      <span class="kwrd">try</span></pre>
<pre><span class="lnum"> 280:  </span>      {</pre>
<pre><span class="lnum"> 281:  </span>        InvokePattern pushButton =</pre>
<pre><span class="lnum"> 282:  </span>          (InvokePattern)buttonName.GetCurrentPattern(InvokePattern.Pattern);</pre>
<pre><span class="lnum"> 283:  </span>        pushButton.Invoke();</pre>
<pre><span class="lnum"> 284:  </span>      }</pre>
<pre><span class="lnum"> 285:  </span>      <span class="kwrd">catch</span> (ElementNotEnabledException)</pre>
<pre><span class="lnum"> 286:  </span>      {</pre>
<pre><span class="lnum"> 287:  </span>        <span class="kwrd">throw</span> <span class="kwrd">new</span> ElementNotEnabledException(<span class="str">"Button is not enabled."</span>);</pre>
<pre><span class="lnum"> 288:  </span>      }</pre>
<pre><span class="lnum"> 289:  </span>      <span class="kwrd">catch</span> (InvalidOperationException)</pre>
<pre><span class="lnum"> 290:  </span>      {</pre>
<pre><span class="lnum"> 291:  </span>        <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Button not found."</span>);</pre>
<pre><span class="lnum"> 292:  </span>      }</pre>
<pre><span class="lnum"> 293:  </span>    }</pre>
<pre><span class="lnum"> 294:  </span></pre>
<pre><span class="lnum"> 295:  </span>    <span class="rem">/// &lt;summary&gt;</span></pre>
<pre><span class="lnum"> 296:  </span>    <span class="rem">/// Generic method to close an AUT process</span></pre>
<pre><span class="lnum"> 297:  </span>    <span class="rem">/// &lt;/summary&gt;</span></pre>
<pre><span class="lnum"> 298:  </span>    <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> CloseAutProcess(Process autProcess)</pre>
<pre><span class="lnum"> 299:  </span>    {</pre>
<pre><span class="lnum"> 300:  </span>      <span class="kwrd">try</span></pre>
<pre><span class="lnum"> 301:  </span>      {</pre>
<pre><span class="lnum"> 302:  </span>        autProcess.CloseMainWindow();</pre>
<pre><span class="lnum"> 303:  </span>        autProcess.WaitForExit(500);</pre>
<pre><span class="lnum"> 304:  </span>        <span class="kwrd">if</span> (!autProcess.HasExited)</pre>
<pre><span class="lnum"> 305:  </span>        {</pre>
<pre><span class="lnum"> 306:  </span>          autProcess.Kill();</pre>
<pre><span class="lnum"> 307:  </span>        }</pre>
<pre><span class="lnum"> 308:  </span>      }</pre>
<pre><span class="lnum"> 309:  </span>      <span class="kwrd">catch</span> (InvalidOperationException)</pre>
<pre><span class="lnum"> 310:  </span>      {</pre>
<pre><span class="lnum"> 311:  </span>        <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Specified process not found."</span>);</pre>
<pre><span class="lnum"> 312:  </span>      }</pre>
<pre><span class="lnum"> 313:  </span>    }</pre>
<pre><span class="lnum"> 314:  </span>    <span class="preproc">#endregion</span> HELPER METHODS</pre>
<pre><span class="lnum"> 315:  </span>  }</pre>
<pre><span class="lnum"> 316:  </span>}</pre>
</div>
<p><!--.csharpcode, .csharpcode pre { 	font-size: x-small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/05/19/test-automation-beyond-rudimentary-script-lets/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Automation isn&#8217;t bad; bad automation is bad!</title>
		<link>http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/</link>
		<comments>http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/#comments</comments>
		<pubDate>Sun, 08 May 2011 13:18:48 +0000</pubDate>
		<dc:creator>Bj Rollison</dc:creator>
				<category><![CDATA[Test Automation]]></category>

		<guid isPermaLink="false">http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/</guid>
		<description><![CDATA[As I write this I am flying at 38,000 feet or so across the Artic Circle. I am on my way to the Netherlands where I have been invited to keynote the TestNet conference. I am very excited to attend this conference and to meet new friends and connect with old ones. But, the guy [...]]]></description>
			<content:encoded><![CDATA[<p>As I write this I am flying at 38,000 feet or so across the Artic Circle. I am on my way to the Netherlands where I have been invited to keynote the <a href="http://www.testnet.org/" target="_blank">TestNet</a> conference. I am very excited to attend this conference and to meet new friends and connect with old ones. But, the guy beside me is hacking his head off (God, I hope he is not contagious). There is an elderly gentleman sitting in front of me constantly readjusting his chair up and back, up and back. And, I think the woman behind me has a bladder problem because about every 15 minutes she slams her tray up into my back, and jerks violently on the back of my chair as she lifts herself up. Fortunately on this flight there are no crying children within earshot (as of yet anyway). Since I don’t sleep well on airplanes without being in a drug induced stupor I thought I would hammer some thoughts on automated testing.</p>
<p>I am still surprised how often I hear people disparage automated testing. It’s almost like a rampant phobia similar to fear of water, fear of heights, or fear of the unknown. What leads people to proclaim disingenuous tripe that I sometimes read on blogs, or discussion forums? For example, the other day I read “automation will not find any new bugs.” Well, I guess that is the experience of some folks. But of course we could say that any inexperienced, untrained, or non-thinking person who uses a tool is likely to misuse the tool and get hurt, do damage, or come to a false conclusion that the tool doesn’t work. Automated tests are not bad; stupid automated tests are bad. If your test automation is not providing value don’t blame the tool…maybe a little introspection is in order.</p>
<h2>All Automation Is Not Equal</h2>
<p>The first problem in almost any discussion about automation is the lack of context. Automated tests are different and when done well can be effective at helping us identify some types of functional issues. An automated unit test suite can be a powerful tool for developers during refactoring. Of course some bugs escape unit testing because unit tests are not intended to be comprehensive. I have long been a proponent of functional testing at the API layer and this is where my team plays today. I see first hand the value of our automated tests in the early identification of potentially critical functional issues during our continuous integration process. While this level of testing is effective in helping prevent build breaks, it is not comprehensive and does not test the behavior of the application through the user interface. And then, comes GUI automation.</p>
<h2>Why (Most) GUI Automation Sucks!</h2>
<p>Although I am not a big fan of GUI automation I have done it, I teach it, and I know that it can be of value in many ways. Last week I was discussing reasons for intermittent failures in test automation with a colleague. Of course, a large number of intermittent failures occur in GUI automation for a variety of reasons (that’s a different post somewhere in the future). But, as I was reviewing the test code something dawned on me…the tests sucked!</p>
<p>It seems that when many people sit down to automate a GUI test (using just about any test <a href="http://www.testingmentor.com/imtesty/2009/11/18/programming-paradigms-in-test-automation/" target="_blank">automation paradigm</a>) the automation is about a brain dead as a zombie. I suspect that when folks sit down to automate a GUI test the first thing they do is step through the UI. Then the tester automates each step until he/she arrives at some final state and validates the results with an equally brain dead oracle. In other words a lot of GUI automation is comprised of simplistic scripts that attempts to emulate human behavior patterns rather than being a well-designed test that capitalizes on the power of the computer as a tool.</p>
<p><a href="http://testingmentor.com/imtesty/wp-content/uploads/2011/05/image.png"><img style="background-image: none; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; padding-top: 0px; border-width: 0px;" title="image" src="http://testingmentor.com/imtesty/wp-content/uploads/2011/05/image_thumb.png" border="0" alt="image" width="240" height="92" align="left" /></a>Let’s look at a simple example; the “Hello” application. The application takes a first name string then displays a message box that reads “Hello [name]!”</p>
<p>&nbsp;</p>
<p>Here is an example of a brain dead automated test that we might find if we asked someone to automate this “feature.”</p>
<div class="csharpcode">
<pre><span class="lnum">   1:  </span><span class="kwrd">namespace</span> BraindDeadAutomatedGuiTest</pre>
<pre><span class="lnum">   2:  </span>{</pre>
<pre><span class="lnum">   3:  </span>  <span class="kwrd">class</span> Program</pre>
<pre><span class="lnum">   4:  </span>  {</pre>
<pre><span class="lnum">   5:  </span>    [STAThread]</pre>
<pre><span class="lnum">   6:  </span>    <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)</pre>
<pre><span class="lnum">   7:  </span>    {</pre>
<pre><span class="lnum">   8:  </span>      <span class="rem">// Launch the AUT</span></pre>
<pre><span class="lnum">   9:  </span>      Process testAut = <span class="kwrd">new</span> Process();</pre>
<pre><span class="lnum">  10:  </span>      testAut.StartInfo.FileName = <span class="str">"Hello.exe"</span>;</pre>
<pre><span class="lnum">  11:  </span>      testAut.Start();</pre>
<pre><span class="lnum">  12:  </span></pre>
<pre><span class="lnum">  13:  </span>      <span class="rem">// Hard coded sleep to give AUT time to instantiate</span></pre>
<pre><span class="lnum">  14:  </span>      System.Threading.Thread.Sleep(1000);</pre>
<pre><span class="lnum">  15:  </span></pre>
<pre><span class="lnum">  16:  </span>      <span class="rem">// Use key mnemonic to set focus to textbox </span></pre>
<pre><span class="lnum">  17:  </span>      <span class="rem">// Often this is skipped because people assume this textbox has focus</span></pre>
<pre><span class="lnum">  18:  </span>      SendKeys.SendWait(<span class="str">"%f"</span>);</pre>
<pre><span class="lnum">  19:  </span></pre>
<pre><span class="lnum">  20:  </span>      <span class="rem">// Enter hard-coded test data</span></pre>
<pre><span class="lnum">  21:  </span>      SendKeys.SendWait(<span class="str">"Boob"</span>);</pre>
<pre><span class="lnum">  22:  </span></pre>
<pre><span class="lnum">  23:  </span>      <span class="rem">// Tab to the OK key and press</span></pre>
<pre><span class="lnum">  24:  </span>      SendKeys.SendWait(<span class="str">"{Tab}"</span>);</pre>
<pre><span class="lnum">  25:  </span>      SendKeys.SendWait(<span class="str">"{ENTER}"</span>);</pre>
<pre><span class="lnum">  26:  </span></pre>
<pre><span class="lnum">  27:  </span>      <span class="rem">// Another hard coded sleep to make sure messagebox appears</span></pre>
<pre><span class="lnum">  28:  </span>      System.Threading.Thread.Sleep(1000);</pre>
<pre><span class="lnum">  29:  </span></pre>
<pre><span class="lnum">  30:  </span>      <span class="rem">// Now comes the brain dead oracle</span></pre>
<pre><span class="lnum">  31:  </span>      Clipboard.Clear();</pre>
<pre><span class="lnum">  32:  </span></pre>
<pre><span class="lnum">  33:  </span>      <span class="rem">// Capture the messagebox text</span></pre>
<pre><span class="lnum">  34:  </span>      SendKeys.SendWait(<span class="str">"^(+c)"</span>);</pre>
<pre><span class="lnum">  35:  </span></pre>
<pre><span class="lnum">  36:  </span>      <span class="rem">// Now get the text from the clipboard and see if it contains the</span></pre>
<pre><span class="lnum">  37:  </span>      <span class="rem">// hard-coded test data; often I see people compare strings</span></pre>
<pre><span class="lnum">  38:  </span>      <span class="rem">// verbatim which is a recipe for false positives &amp; maintenance </span></pre>
<pre><span class="lnum">  39:  </span>      <span class="kwrd">string</span> actualResult = Clipboard.GetText(TextDataFormat.Text);</pre>
<pre><span class="lnum">  40:  </span>      <span class="kwrd">if</span> (actualResult.Contains(<span class="str">"Boob"</span>))</pre>
<pre><span class="lnum">  41:  </span>      {</pre>
<pre><span class="lnum">  42:  </span>        Console.WriteLine(<span class="str">"Pass"</span>);</pre>
<pre><span class="lnum">  43:  </span>      }</pre>
<pre><span class="lnum">  44:  </span>      <span class="kwrd">else</span></pre>
<pre><span class="lnum">  45:  </span>      {</pre>
<pre><span class="lnum">  46:  </span>        Console.WriteLine(<span class="str">"Fail"</span>);</pre>
<pre><span class="lnum">  47:  </span>      }</pre>
<pre><span class="lnum">  48:  </span></pre>
<pre><span class="lnum">  49:  </span>      <span class="rem">// Clean up - kill the messagebox then close the AUT</span></pre>
<pre><span class="lnum">  50:  </span>      SendKeys.SendWait(<span class="str">"{ESC}"</span>);</pre>
<pre><span class="lnum">  51:  </span>      testAut.CloseMainWindow();</pre>
<pre><span class="lnum">  52:  </span>    }</pre>
<pre><span class="lnum">  53:  </span>  }</pre>
<pre><span class="lnum">  54:  </span>}</pre>
</div>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } -->Now, you might be saying that this is an absurd example, Really? I don’t think so. I see this sort of automation all the time in examples on the web and at conferences. For example, how often have you ever seen an example along the lines of:</p>
<ol>
<li>Launch IE (or some other browser)</li>
<li>Navigate to “http://google.com”</li>
<li>Enter “Ruby” in test box</li>
<li>Press Search button</li>
<li>Verify “Ruby” appears in results</li>
</ol>
<p>Now, if you really expect this type of automation to add value then you should really consider a new line of work…perhaps assembly line work.</p>
<p>Well, we are about 1 1/2 hours out from Amsterdam and breakfast is about to be served, so I am going to wrap this up now. Yes, I learned a long time ago not to complain if I don’t at least offer a solution (whining is never appreciated). So, later this week, perhaps on the trip home on Wednesday, I will suggest one possible solution to automate even this simple app that could provide value by increasing test coverage and potentially exposing some errant behavior (bugs).</p>
<p>(<em>Oh…and by the way, while writing this a little dirt merchant in pajamas and a pacifier sticking out of his pie-hole started doing laps through the cabin banging off the sides of the chairs…but at least he wasn’t crying!</em>)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.testingmentor.com/imtesty/2011/05/08/automation-isnt-bad-bad-automation-is-bad/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

