<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>I. M. Testy : The Professional Tester</title><link>http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx</link><description>Tags: The Professional Tester</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Localization Testing Part III</title><link>http://blogs.msdn.com/imtesty/archive/2009/11/03/localization-testing-part-iii.aspx</link><pubDate>Tue, 03 Nov 2009 19:36:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9916926</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9916926.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9916926</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9916926</wfw:comment><description>&lt;P&gt;Part 1 provided an overview of localization class issues, and Part II discussed issues with non-translated strings in a localized product and gave some helpful hints to manage that problem during the software development lifecycle. In Part III I will cover various issues with access key mnemonics. An access key mnemonic is the underlined letter on a menu or control that corresponds to a key on the native keyboard layout for a particular language. &lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Missing &amp;amp; duplicate access key mnemonics&lt;/STRONG&gt; &lt;/H2&gt;
&lt;P&gt;Interestingly enough, most localization tools have built in tools to test for duplicate key mnemonics; however, missing or duplicate access key mnemonics is another significant issue in localization testing, and also affects the English language version as well. Duplicate or missing key mnemonics can adversely affect the usability of software because it impacts the ability of the user to easily access or invoke commonly repeated functions using the keyboard. Duplicate or missing duplicate key mnemonics can also negatively impact the software’s ability to meet certain accessibility requirements. &lt;/P&gt;
&lt;P&gt;Although missing or duplicate access key mnemonics are sometimes caused by poorly designed dialogs with an overabundance of controls, there are other factors that can cause duplicate key mnemonics. For example, some controls may dynamically appear in some dialogs in specific machine states. These dynamically generated controls may also come from a file that is different than the file which generated the dialog. Another reason for duplicate key mnemonics could also be dynamically generated key mnemonic assignments, which are especially problematic in situations where a dialog contains a mixture of statically assigned key mnemonics and dynamically generated key mnemonics.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/dup%20keys_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/dup%20keys_2.jpg"&gt;&lt;IMG style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; WIDTH: 313px; DISPLAY: inline; HEIGHT: 369px; BORDER-TOP: 0px; BORDER-RIGHT: 0px" title="dup keys" border=0 alt="dup keys" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/dup%20keys_thumb.jpg" width=480 height=502 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/dup%20keys_thumb.jpg"&gt;&lt;/A&gt; &lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/missing_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/missing_2.jpg"&gt;&lt;IMG style="BORDER-RIGHT-WIDTH: 0px; MARGIN: 5px; WIDTH: 237px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; HEIGHT: 366px; BORDER-LEFT-WIDTH: 0px" title="Missing access key mnemonics" border=0 alt="Missing access key mnemonics" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/missing_thumb.jpg" width=495 height=496 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/missing_thumb.jpg"&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Manual testing for missing or duplicate key mnemonics is especially labor intensive, and finding ways to automate this testing will save countless hours of time sitting in front of a computer checking menus and dialogs. There is also a large probability of missing duplicate key mnemonic assignment problems using manual testing methods because eyes get tired, people get bored, and some keys are grayed out (as in the illustration below) or may not be present in certain machine states. Fortunately, there are several automation tools that detect duplicate key mnemonic problems and automated detection is more effective than manual test approaches. For example, &lt;A href="http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.accesskeyproperty.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.accesskeyproperty.aspx"&gt;Automation.Element.AccessKeyProperty&lt;/A&gt; in the UIAutomation class library in C# is one approach to more efficiently test access key mnemonics.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Access key mnemonic assignments&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key1_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key1_2.jpg"&gt;&lt;IMG style="BORDER-RIGHT-WIDTH: 0px; MARGIN: 5px; WIDTH: 192px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; HEIGHT: 315px; BORDER-LEFT-WIDTH: 0px" title="Inappropriate key assignments" border=0 alt="Inappropriate key assignments" align=right src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key1_thumb.jpg" width=294 height=407 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key1_thumb.jpg"&gt;&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key_2.jpg"&gt;&lt;IMG style="BORDER-RIGHT-WIDTH: 0px; MARGIN: 5px; WIDTH: 300px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; HEIGHT: 295px; BORDER-LEFT-WIDTH: 0px" title="Inappropriate key assignments" border=0 alt="Inappropriate key assignments" align=left src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key_thumb.jpg" width=461 height=400 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key_thumb.jpg"&gt;&lt;/A&gt; As a general rule of thumb (heuristic), key mnemonics should be assigned to characters mapped to the default state of the keyboard for each particular language. For the Latin 1 family of languages, access key mnemonics should generally not be assigned to non-ASCII characters; even if that particular character is accessible on the default state keyboard layout for a particular language. Certain, access key mnemonics should never be assigned to a character glyph that is formed through combining characters used in languages such as Thai and Arabic. Also, punctuation characters should never be assigned as access key mnemonics.&lt;/P&gt;
&lt;P&gt;Of course, the default keyboard layout for many non-Latin 1 languages only contain characters in the native script for that language, and assigning non-ASCII characters as access key mnemonics may be the only choice. However, &lt;/P&gt;
&lt;P&gt;Japanese hiragana and katakana glyphs, Korean Hangeul glyphs, and all East Asian ideographic glyphs are invalid character assignments for access key mnemonics. The default keyboard layout for most East Asian languages (Japanese, Korean, Simplified Chinese, and Traditional Chinese) is the standard keyboard layout similar to the US English keyboard. In the above example, their is no way for a Japanese user to access the ‘My Computer’ (マイ コンピユータ）menu item because it is using a katakana character as an access key mnemonic (which violates several guidelines for access key mnemonic assignment). Also, the standard key mnemonic guidelines described below should be used for all East Asian language versions for consistency and backwards compatibility. &lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key%202_6.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key%202_6.jpg"&gt;&lt;IMG style="BORDER-RIGHT-WIDTH: 0px; MARGIN: 5px; WIDTH: 287px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; HEIGHT: 362px; BORDER-LEFT-WIDTH: 0px" title="Inappropriate key assignments" border=0 alt="Inappropriate key assignments" align=left src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key%202_thumb_2.jpg" width=459 height=431 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/key%202_thumb_2.jpg"&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/keys_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/keys_2.jpg"&gt;&lt;IMG style="BORDER-RIGHT-WIDTH: 0px; MARGIN: 5px; WIDTH: 444px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; HEIGHT: 378px; BORDER-LEFT-WIDTH: 0px" title="Inappropriate key assignments in East Asian languages" border=0 alt="Inappropriate key assignments in East Asian languages" align=right src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/keys_thumb.jpg" width=595 height=460 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartIII_6ACD/keys_thumb.jpg"&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Another general guideline to follow for access key mnemonic assignments is to avoid the lower case Latin letters ‘g’ ‘y’ ‘p’ ‘q’ and ‘j’ because there is a high probability of confusion especially with high display resolutions especially with the letters i and l, and q and g. These letters could also be hard to discern on high resolution desktop settings as well. If the number of controls on a single dialog or in a menu list require usage of inappropriate key mnemonics, then perhaps the problem is the design of the dialog.&lt;/P&gt;
&lt;P mce_keep="true"&gt;East Asian language versions should use the identical key mnemonics as the English language version. The characters assigned as key mnemonics in the East Asian language are capitalized, enclosed within parenthesis, and positioned at the end of the translated string. Even when a key mnemonic appears within words or acronyms which are not translated or transliterated into the target the key mnemonic should be relocated to the end of the string and enclosed within parenthesis for consistency.&lt;/P&gt;
&lt;P&gt;The character assigned as the key mnemonic should be capitalized because many East Asian computer users use an English key keyboard, and for users whose native language does not frequently employ Latin characters it is much easier for those users to visually identify key mnemonics which are capitalized with keys on the keyboard which are also capital case characters.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Accelerator&amp;nbsp;Keys&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&amp;nbsp;Accelerator keys are commonly refered to as shortcut keys.&amp;nbsp; Accelerator&amp;nbsp;&lt;SPAN style="FONT-FAMILY: Tahoma; COLOR: black; FONT-SIZE: 10pt; language: en-US; mso-ascii-font-family: Tahoma; mso-fareast-font-family: +mn-ea; mso-bidi-font-family: +mn-cs; mso-color-index: 1; mso-font-kerning: 12.0pt"&gt;keys are keys (such as F1 - F12 and Esc) or key combinations (Ctrl + Shift + B, or Ctrl C)&amp;nbsp;that allow users to evoke certain functions without navigating the software menus via access keys or using the mouse to click button controls on a toolbar.&amp;nbsp;&lt;A href="http://www.roboxpress.com/technology/tips-and-tricks/windows-7-keyboard-shortcuts-accelerator-keys-hotkeys/" mce_href="http://www.roboxpress.com/technology/tips-and-tricks/windows-7-keyboard-shortcuts-accelerator-keys-hotkeys/"&gt;Here&lt;/A&gt; is&amp;nbsp;good source for common Windows accelerator keys, and &lt;A href="https://blogs.msdn.com/jensenh/archive/2006/02/14/531801.aspx" mce_href="https://blogs.msdn.com/jensenh/archive/2006/02/14/531801.aspx"&gt;here&lt;/A&gt; is one for common accelerator keys for Office products.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-FAMILY: Tahoma; COLOR: black; FONT-SIZE: 10pt; language: en-US; mso-ascii-font-family: Tahoma; mso-fareast-font-family: +mn-ea; mso-bidi-font-family: +mn-cs; mso-color-index: 1; mso-font-kerning: 12.0pt"&gt;Shortcut key combinations are common throughout all language versions. Contrary to the Wikipedia entry on the subject&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="FONT-FAMILY: Tahoma; COLOR: black; FONT-SIZE: 10pt; language: en-US; mso-ascii-font-family: Tahoma; mso-fareast-font-family: +mn-ea; mso-bidi-font-family: +mn-cs; mso-color-index: 1; mso-font-kerning: 12.0pt"&gt;some language versions localize the&amp;nbsp;letter key (not a mnemonic...it is not underlined). For example,&amp;nbsp; in German the Ctrl key is localized as "Strg" and, dispite it is generally frowned upon to change the&amp;nbsp;ASCII upper case letter assigned to an accelerator key combination&amp;nbsp;the Spanish versions of Office use Ctrl+G (&lt;EM&gt;Guardar&lt;/EM&gt;) for Save instead of Ctrl+S, and Ctrl+N (&lt;EM&gt;Negrita&lt;/EM&gt;) for Bold instead of Ctrl+B. Also,&amp;nbsp;letter keys&amp;nbsp;used as&amp;nbsp;part&amp;nbsp;of&amp;nbsp;an accelerator key&amp;nbsp;combination are capitalized. East Asian language versions use Ctrl to designate the Control key. Also, accelerator key combinations do&amp;nbsp;include an elipsis after the letter.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-FAMILY: Tahoma; COLOR: black; FONT-SIZE: 10pt; language: en-US; mso-ascii-font-family: Tahoma; mso-fareast-font-family: +mn-ea; mso-bidi-font-family: +mn-cs; mso-color-index: 1; mso-font-kerning: 12.0pt"&gt;&lt;IMG style="WIDTH: 244px; HEIGHT: 186px" src="http://blogs.msdn.com/photos/imtesty/images/9917775/secondarythumb.aspx" width=106 height=140 mce_src="http://blogs.msdn.com/photos/imtesty/images/9917775/secondarythumb.aspx"&gt;&amp;nbsp; &lt;IMG style="WIDTH: 284px; HEIGHT: 181px" src="http://blogs.msdn.com/photos/imtesty/images/9917774/500x282.aspx" width=284 height=181 mce_src="http://blogs.msdn.com/photos/imtesty/images/9917774/500x282.aspx"&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;In Part IV I will discuss common layout issues such as clipping and truncation.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9916926" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Localization+Testing/default.aspx">Localization Testing</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Internationalization+Testing/default.aspx">Internationalization Testing</category></item><item><title>Localization Testing – Part II</title><link>http://blogs.msdn.com/imtesty/archive/2009/10/30/localization-testing-part-ii.aspx</link><pubDate>Fri, 30 Oct 2009 19:34:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9915422</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9915422.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9915422</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9915422</wfw:comment><description>&lt;P&gt;I should be of no surprise to anyone that localization testing generally focuses on changes in the user interface, although as mentioned in the &lt;A href="http://blogs.msdn.com/imtesty/archive/2009/10/27/localization-testing-part-1.aspx" target=_blank mce_href="http://blogs.msdn.com/imtesty/archive/2009/10/27/localization-testing-part-1.aspx"&gt;previous post&lt;/A&gt; these are not the only changes necessary to adapt a product to a specific target market. But, the most common category of localization class bug are usability or behavioral type issues that do involve the user interface. Bugs in this category generally include un-localized or un-translated text, key mnemonics, clipped or truncated text labels and other user interface controls, incorrect tab order, and layout issues. Fortunately, the majority of problems in this category do not require a fix in the software’s functional or business layer. Also, the majority of problems in this category do not require any special linguistic skills in order to identify, and in some cases, an automated approach can be even more effective than the human eye (more on that later).&lt;/P&gt;
&lt;P&gt;Perhaps the most commonly reported issue in this category is “un-localized” or un-translated textual string. Unfortunately, in many cases un-translated strings is also an over-reported problem that only serves to flood the the defect tracking database with unnecessary bugs. Translating textual strings is a demanding task, and made even more difficult when there are constant changes in the user interface or contextual meaning of messages early in the product life cycle. Over-reporting of un-translated text too early in the product cycle only serves to artificially inflate the bug count, and causes undue pressure and creates extra work for the localization team.&lt;/P&gt;
&lt;P&gt;Identifying this type of bugs is actually pretty easy. Here’s a simple heuristic; if you are testing a non-English language version in a language you are not familiar with and you can clearly read the textual string in English it is probably not localized or translated into the target language. The illustration below provides a pretty good example of this general rule of thumb. A tester doesn’t have to read German to realize that the text in the label control under the first radio button is not German.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartII_7C2D/1_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartII_7C2D/1_2.jpg"&gt;&lt;IMG style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; WIDTH: 612px; DISPLAY: inline; HEIGHT: 392px; BORDER-TOP: 0px; BORDER-RIGHT: 0px" title=1 border=0 alt=1 src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartII_7C2D/1_thumb.jpg" width=790 height=479 mce_src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/LocalizationTestingPartII_7C2D/1_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;There are several causes of un-localized text strings to appear in dialogs and other areas of the user interface. For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Worse case scenario is that the string is hard-coded into the source files&lt;/LI&gt;
&lt;LI&gt;Perhaps localizers did not have enough time to completely process all strings in a particular file&lt;/LI&gt;
&lt;LI&gt;Perhaps this is a new string in a file localizers thought was 100% localized&lt;/LI&gt;
&lt;LI&gt;Strings displayed in some dialogs come from files other than the file that generates the dialog, and the localization team has not process that file&lt;/LI&gt;
&lt;LI&gt;And, sometimes (usually not often), a string may simply be overlooked during the localization process&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Testing for un-localized text is often a manually intensive process of enumerating menus, dialogs, and other user interface dialogs, message boxes and form, and form elements.&amp;nbsp; But, if the textual strings are located in a separate resource file (as they should be), a quick scan of resource files might more quickly reveal un-translated textual strings. Of course, there is little context in the resource file, and I also hope the localization team is reviewing their own work as well prior to handing it over to test.&lt;/P&gt;
&lt;P&gt;Also, here are a few suggestions that might help focus localization testing efforts early in the project milestone and reduce the number of ‘known’ or false-positive un-translated text bugs being reported:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Ask the localization team to report the percentage of translation completion by file or module for each test build. Early in the development lifecycle only modules that are reported to be 100% complete which appear to have un-translated text should be reported as valid bugs. Of course, sometimes some strings are used in multiple modules, or may be coming from external resources. But, especially early in the development lifecycle reporting a gaggle of un-translated text bugs is simply “make work.” As the life cycle starts winding down…all strings are fair game for bug hunters!&lt;/LI&gt;
&lt;LI&gt;Testers should use tools such a Spy++ or Reflector to help identify the module or other resources, and the unique resource ID for the problematic string or resource. This is much better then than simply attaching an image of the offending dialog to a defect report. Identifying the module and the specific resource ID number allows the localization team to affect a quick fix instead of having to search for the dialog through repro steps and track down the problem.&lt;/LI&gt;
&lt;LI&gt;Also remember that not all textual strings are translated into a specific target language. Registered or trademarked product names are often not translated into different languages. In case of doubt, ask the localization team if a string that appears un-localized is a ‘true’ problem or not.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Unlocalized strings&amp;nbsp;usually due to hard coded strings also&amp;nbsp;tend to&amp;nbsp;occur in&amp;nbsp;menu items. This is especially true in the Windows Start menu or sub-menu items&amp;nbsp;hard-coded&amp;nbsp;in the&amp;nbsp;INF or&amp;nbsp;other installation/setup files. For example,&amp;nbsp;the image on the right&amp;nbsp;shows a common problem on European versions of Windows.&amp;nbsp;Many European language versions &amp;nbsp;localize the name of the Program Files folder, and&amp;nbsp;the menu item in the start menu. But, often times when we install an English language version of software to Windows it creates a new "Programs" menu item (and even a new Program Files directory, rather than detecting the default folder to install to.&amp;nbsp;In the&amp;nbsp;example on the left, the string Accessories is a hard-coded folder name. But, there is another issue as well. This illustrates not only a problem with&amp;nbsp;the non-translated string "Accesssories," but also shows one full-width Katakana string for 'Accessories' and another half-width string. &lt;/P&gt;
&lt;P mce_keep="true"&gt;&lt;IMG src="http://blogs.msdn.com/photos/imtesty/images/9919409/500x282.aspx" mce_src="http://blogs.msdn.com/photos/imtesty/images/9919409/500x282.aspx"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;IMG src="http://blogs.msdn.com/photos/imtesty/images/9919410/500x282.aspx" mce_src="http://blogs.msdn.com/photos/imtesty/images/9919410/500x282.aspx"&gt;&lt;/P&gt;
&lt;P&gt;In part 3 I will discuss another often problematic area in localization….key mnemonics.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9915422" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item><item><title>Localization Testing: Part 1</title><link>http://blogs.msdn.com/imtesty/archive/2009/10/27/localization-testing-part-1.aspx</link><pubDate>Tue, 27 Oct 2009 06:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9913347</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9913347.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9913347</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9913347</wfw:comment><description>&lt;P&gt;When I first joined Microsoft 15 years ago I was on the Windows 95 International team. Our team was responsible for reducing the delta between the release of the English version and the Japanese version to 90 days, and I am very proud to say that we achieved that goal and Windows 95 took Japan by storm. It was so amazing that even people without computers were lined up outside of sales outlets waiting to purchase a copy of Windows 95. The growth of personal computers in Japan shot through the roof over the next few years. Today the Chinese market is exploding, and eastern European nations are experiencing unprecedented growth as well.&amp;nbsp; While the demand for the English language versions of our software still remains high, many of our customers are demanding software that is ‘localized’ to accommodate the customers national conventions, language, and even locally available hardware. Although much of the Internets content is in English, non-English sites on the web are growing, and even &lt;A href="http://icann.org/en/announcements/announcement-2-30sep09-en.htm" target=_blank mce_href="http://icann.org/en/announcements/announcement-2-30sep09-en.htm"&gt;ICANN is considering allowing international domain names&lt;/A&gt; that contain non-ASCII characters this week in Seoul, Korea.&lt;/P&gt;
&lt;P&gt;But, a lot has changed in how we develop software to support international markets. International versions of Windows 95 were developed on a forked code base. Basically, this means the source code contained #ifdefs to instruct the compiler to compile different parts of the source code depending on the language family. From a testing perspective this is a nightmare, because if the underlying code base of a localized version is fundamentally different than the base (US English) version then the testing problem is magnified because there is a lot of functionality that must be retested. Fortunately today, much software being produced is based on a single-worldwide binary model. (I briefly explained the single world wide binary concept at a &lt;A href="http://www.sasqag.org/pastmeetings/LocalizabilityTesting.ppt" target=_blank&gt;talk&lt;/A&gt; in 1991, and Michael Kaplan talks about the advantages &lt;A href="http://blogs.msdn.com/michkap/archive/2006/05/01/587340.aspx" target=_blank mce_href="http://blogs.msdn.com/michkap/archive/2006/05/01/587340.aspx"&gt;here&lt;/A&gt;.) In a nutshell, a single worldwide binary model is a development approach in which any functionality any user anywhere in the world might need is codified in the core source code so we don’t need to modify the core code once it is compiled to include some language/locale specific functionality.&amp;nbsp; For example, it was impossible to input Japanese text into Notepad on an English version of Windows 95 using an Input Method Editor (IME); I needed the localized Japanese version. But, on the English version of Windows Xp, Vista, or Windows 7 all I have to do is install the appropriate keyboard drivers and font files and expose the IME functionality. In fact, these days I can map my keyboard to over 150 different layouts and install fonts for all defined Unicode characters on any language version of the Windows operating system. &lt;/P&gt;
&lt;P&gt;The big advantage of the single worldwide binary development model is that it allows us to differentiate between globalization testing and localization testing.&amp;nbsp; At Microsoft we define globalization as “&lt;EM&gt;the process of designing and implementing a product and/or content (including text and non-text elements) so that it can accommodate any locale market (locale).&lt;/EM&gt;” And, we define localization as “&lt;EM&gt;the process of adapting a product and/or content to meet the language, cultural and political expectations and/or requirements of a specific target market&lt;/EM&gt;.” This means we can better focus on the specific types of issues that each testing approach is most effective at identifying. For localization testing, this means we can focus on the specific things that change in the software during the “adaptation processes” to localize a product for each specific target market. &lt;/P&gt;
&lt;P&gt;The most obvious adaptation process is the ‘localization’ or actually the translation of the user interface textual elements such as menu labels, text in label controls, and other string resources that are commonly exposed to the end user. However, the translation of string resources is not the only thing that occurs during the localization process. The localization processes that are required to adapt a software product to a specific local may also include other changes such as font files and drivers installed by default, registry keys set differently, drivers to support locale specific hardware devices, etc. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;3 Categories of Localization Class Bugs&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;I am a big fan of developing a bug taxonomic hierarchy as part of my defect tracking database as a best practice because it better enables me to analyze bug data more efficiently. If I see a particular category of bug or a type of bug in a category that is being reported a lot, then perhaps we should find ways to prevent or at least minimize the problem from occurring later in the development lifecycle. After years of analyzing different bugs, I classified all localization class bugs into 3 categories; functionality, behavioral/usability, and linguistic quality. &lt;/P&gt;
&lt;P&gt;Functionality type bugs exposed in localized software affect the functionality of the software and require a fix in the core source code. Fortunately, with a single worldwide binary development model where the core functionality is separated from the user interface the number of bugs in this category of localization class bugs is relatively small.&amp;nbsp; Checking the appropriate registry keys are set and files are installed in a new build is reasonably straight forward and should be built into the build verification test (BVT) suite. Other types of things that should be checked include application and hardware compatibility. It is important to identify these types of problems early because they are quite costly to correct, and can have a pretty large ripple effect on the testing effort.&lt;/P&gt;
&lt;P&gt;Behavioral and usability issues primarily impact the usefulness or aesthetic quality of the user interface elements. Many of the problems in this category do not require a fix in the core functional layer of the source code. The types of bugs in this category include layout issues, un-translated text,&amp;nbsp; key mnemonic issues, and other problems that are often fixed in the user interface form design, form class, or form element properties. This category of problems often accounts for more than 90% of all localization class bugs. Fortunately, the majority of problems in this category do not require any special linguistic skills; a careful eye for detail is all that is required to expose even the most discrete bugs in this category.&lt;/P&gt;
&lt;P&gt;The final category of localization class bug is linguistic quality. This category of bugs are primarily mis-translations. Obviously, the ability to read the language being tested is required to identify most problems in this category of errors. We found testers spent a lot of time looking for this type of bug, but later found the majority of linguistic quality type issues reported were resolved as won’t fix. There are many reasons for this, but here is my position on this type of testing….Stop wasting the time of your test team to validate the linguistic quality of the product. If this is a problem then hire a new localizer, hire a team of ‘editors’ to review the work of the localizer, or hire a professional linguistic specialist from the target locale as an auditor. Certainly, if testers see an obvious gaff in a translation then we must report it; however, testers are generally not linguistic experts (even in their native tongue), and I would never advocate hiring testers simply based on linguistic skills nor as a manager would I want to dedicate my valuable testing resources on validating linguistic quality…that’s usually not where their expertise lies, and it probably shouldn’t be.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;What’s Next&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Since behavioral /usability category issues are the most prevalent type of localization class bug this series of posts will focus on localization testing designed to evaluate user interface elements and resources. The next post will expose the often single most reported bug in this category.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9913347" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item><item><title>Adding Variability in Test Case Design</title><link>http://blogs.msdn.com/imtesty/archive/2009/10/20/adding-variability-in-test-case-design.aspx</link><pubDate>Tue, 20 Oct 2009 02:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9909603</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9909603.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9909603</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9909603</wfw:comment><description>&lt;P&gt;I love autumn! Yes, I am definitely a boy of summer and very much prefer warmer weather; however, there is something special about autumn. This past weekend my daughter, and my 2 friends Dongyi and her husband Yuning and I participated in the &lt;A href="http://www.duckdodge.org/index.php" target=_blank mce_href="http://www.duckdodge.org/index.php"&gt;Rum Run&lt;/A&gt; sailboat fun race with an overnight raft up at Bainbridge Island’s Port Madison. Saturday morning was quite rainy, but the wind was blowing 15 knots with gusts to 25 knots and NOAA weather radio announcing gale force warnings in Puget Sound. Wow…what a ride! But, it was actually the rather relaxing sail back to my marina on Sunday morning that rekindled the beauty of autumn in my mind. The bright reds, golden yellows, and pastel browns of the foliage seemed to blend into a collage framed by the darkness of the waters of Puget Sound and the snow covered peaks of the Olympic mountains. The beauty of autumn reminds me about change. A sloughing of the old, the cleansing brought about by the pure white snows, eventually followed by the new and fresh growth that blossoms in spring.&lt;/P&gt;
&lt;P&gt;Just as the earth goes through variable cycles of rejuvenation, we must also continually update our tests, and (more importantly) the test data we use in our test cases to prevent them from becoming stale. Trees shed their leaves in the autumn and new leaves emerge in the spring, but the tree is fundamentally still the same tree. Similarly, a well-designed test case has a unique fundamental purpose and by changing the variables we can grow the value of that test case. Of course, the cycle of change in test data should be dramatically shorter in duration as compared to the seasonal changes of mother earth.&lt;/P&gt;
&lt;P&gt;Here is a simple example of how a well-designed test case using variable test data can increase the value of the information each&amp;nbsp; test iteration provides through increased confidence and also potentially reduce overall risk. In my role at Microsoft I am in a unique position to not only conduct controlled studies, but I can also implement ideas into practice on enterprise level software projects. One experiment I started about 2 years ago involved multiple groups of testers (sessions) located around the world divided into 3 separate control groups. Each control group tested the identical web page that would display the stock price if the user input a valid stock ticker symbol into a single textbox on the page and pressed the OK button. The only difference in the control groups was the instructions to perform single positive test case with the specific purpose of “ensure any valid stock ticker symbol displays the current stock price for the publicly traded stock specified by its symbol.” The purpose of the study was to determine if different cultural and experiential backgrounds impacted the test data used in a test based on the instructions for a test case. The study collected demographic information on the participants as well as specific inputs applied to the web page. Information on the oracle used by the students was collected anecdotally. Step one in each test was identical because we were not interested in how the tester launched the browser. (Of course this assumes there are other tests that test the multitude of ways to launch a browser and navigate to a URL. Also, if the browser failed to launch the test case is blocked.)&lt;/P&gt;
&lt;P&gt;Group 1 was given the most vague instructions for the test case. The instruction was simply:&lt;/P&gt;
&lt;OL&gt;
&lt;OL&gt;
&lt;LI&gt;Launch browser and navigate to [url address]&lt;/LI&gt;
&lt;LI&gt;Enter a valid stock ticker symbol and press the OK button and verify the accuracy of the returned stock price. &lt;/LI&gt;&lt;/OL&gt;&lt;/OL&gt;
&lt;P&gt;The instructions in the test case given to Group 2 were also somewhat vague, but provided a little guidance both on input and oracle.&lt;/P&gt;
&lt;OL&gt;
&lt;OL&gt;
&lt;LI&gt;Launch browser and navigate to [url address]&lt;/LI&gt;
&lt;LI&gt;Enter a valid stock ticker symbol (e.g. “MSFT”) &lt;/LI&gt;
&lt;LI&gt;Press the OK button &lt;/LI&gt;
&lt;LI&gt;Verify the returned stock price is identical to the current stock price listed on the appropriate exchange &lt;/LI&gt;&lt;/OL&gt;&lt;/OL&gt;
&lt;P&gt;Group 3 had similar instructions to Group 2, but the group was given additional guidance as indicated below.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;OL&gt;
&lt;LI&gt;Launch browser and navigate to [url address]&lt;/LI&gt;
&lt;LI&gt;Enter a valid stock ticker symbol from a publicly traded stock listed on any public stock exchange &lt;BR&gt;&lt;BR&gt;Listings of valid stock ticker symbols are on stock exchange web sites such as: &lt;BR&gt;&lt;A href="http://www.nyse.com/" mce_href="http://www.nyse.com"&gt;http://www.nyse.com&lt;/A&gt; &lt;BR&gt;&lt;A title=http://www.eoddata.com/Symbols.aspx href="http://www.eoddata.com/Symbols.aspx" mce_href="http://www.eoddata.com/Symbols.aspx"&gt;http://www.eoddata.com/Symbols.aspx&lt;/A&gt; &lt;BR&gt;&lt;A href="http://www.nasdaq.com/" mce_href="http://www.nasdaq.com"&gt;http://www.nasdaq.com&lt;/A&gt; &lt;BR&gt;&lt;A href="http://www.londonstockexchange.com/" mce_href="http://www.londonstockexchange.com"&gt;http://www.londonstockexchange.com&lt;/A&gt; &lt;BR&gt;&lt;/LI&gt;
&lt;LI&gt;Press the OK Button &lt;/LI&gt;
&lt;LI&gt;Verify the returned stock price is identical to the current stock price listed on the appropriate exchange &lt;/LI&gt;&lt;/OL&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Results&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The results were mostly not surprising, but rather reinforcing. For example, we expected Group 1 to be rather random, but mostly aligned with ticker symbols they were familiar with. Of course, the majority (90%) of stock ticker symbols entered was MSFT and there was no significant difference in cultural background, locale, experience or educational background. (As this study was conducted at Microsoft I am sure there was some bias as to the symbol entered.) What was most interesting was that testers with no formal training (no previous courses in testing, no CS degree, and read less than one discipline specific book) and with more than 2 years of test&amp;nbsp;experience were approximately&amp;nbsp;more likely (25%) to violate the purpose of the test and enter random or completely invalid data as their first action. In other words, instead of executing the required test their initial reaction was to immediately go on a bug hunt. &lt;/P&gt;
&lt;P&gt;In group 2 99% of the participants simply entered the stock ticker symbol “MSFT.” But, what was even more surprising was the fact that one the next day, the same people in that group were given the same exact test, and 95% of them simply reentered MSFT. Perhaps this is laziness, perhaps this is related to the superficial nature of the study, or perhaps this is due to individuals taking the path of least resistance. The percentage of people who entered identical stock ticker symbols on consecutive days was not significantly different between group 1 and group 2.&lt;/P&gt;
&lt;P&gt;It should be no surprise that group 3 had the greatest distribution of variable test data applied to the web page. Demographics had no impact on any of the people who were in group 3. The majority of people in group 3 (78%) would select the first stock exchange listed (regardless of what link it was) but there was no significant overlap in the selected stock ticker symbols. When asked to repeat the test on the next day 83% of the participants selected a different link and and a different symbol. Of those who selected the same link 97% selected a different stock ticker symbol. On the down side, approximately 4% of the people simply took the path of least resistance and input MSFT as the test data on both days of the experiment. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;One of the most common problems I hear about ‘scripted,’ or pre-defined test cases is that they are too prescriptive and not flexible enough to allow the tester to try things. Of course, a well-designed test case is not simply a prescriptive set of steps inputting the same hard coded test data they run over and over. So, in this study we made the assumption that a scripted test case that specified “Enter MSFT in the textbox” would simply result in the tester entering “MSFT” without any thinking on the part of the tester. Hard-coding variable test data is often times the worse possible way to design a test case. &lt;/P&gt;
&lt;P&gt;Vaguely written test cases added some level of variability, but also seemed to increase the probability of the tester executing context free tests outside the scope of the purpose of the test. In fact, what we found was some testers (approx 2%) simply went on a bug hunt and never actually input a valid stock ticker symbol at all during the session.&lt;/P&gt;
&lt;P&gt;A test case that provided only one example that is representative of the type of test data required for the test case produced the least desirable results in this study. I am not sure this would be the case in practice. However, based on this study if I were to outsource execution of a test case similar to that used by group 2 the only thing I could guarantee is that MSFT would definitely be tested numerous times, and the variability of other test data would be extremely limited regardless of the number of testers executing that test or the number of iterations.&lt;/P&gt;
&lt;P&gt;When faced with a virtually infinite number of possibilities for input variables as test data used in either positive or negative tests we need to test as many possibilities as possible given the available resources in order to increase test coverage and reduce overall risk. So, one way increase the coverage of test data while still achieving the specific purpose of the test case is to provide useful resources that help guide the tester while relying on the tester’s creative thinking skills and curiosity to expand the test coverage. &lt;/P&gt;
&lt;P&gt;Of course, we can also increase variability of test data and capture the essence of the tester’s creativity using a similar approach in a well-designed automated test case as well. In fact, a similarly designed automated test case enables us to significantly increase the amount of variable test data that is exercised in order to expand test coverage and increase overall confidence.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9909603" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item><item><title>Randomizing static test data in automated tests</title><link>http://blogs.msdn.com/imtesty/archive/2009/10/11/randomizing-static-test-data-in-automated-tests.aspx</link><pubDate>Sun, 11 Oct 2009 06:19:01 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9905827</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9905827.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9905827</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9905827</wfw:comment><description>&lt;p&gt;A significant percentage of static test data is stored in tabular comma delimited or tab-delimited formats and saved in Excel spreadsheets. Reading in comma or tab-delimited static test data into an automated test is pretty straight forward and there are numerous examples in many programming languages illustrating how to read in these types of test data repositories. Reading in rows of data is the foundation of data-driven automation and definitely has its place in any automation project. &lt;/p&gt;  &lt;p&gt;I am a big proponent of stochastic (random) test data generation that is customized to the context, but I also know that sometimes static test data is useful for establishing baselines and more exact emulation of ‘real-world’ customer-like inputs. But, if the automated test is simply passing the same variable arguments to the same input parameters in the same order over and over again the value of subsequent iterations of that automated test using that static data set diminishes rather quickly. So how can we more effectively utilize static test data in our automated tests?&lt;/p&gt;  &lt;p&gt;One possible solution is to randomly select an argument from a collection of static variables that is passed to the specific input parameter. The advantage of this approach is that it effectively increases the test data permutations in each iteration of the test case. For example, let’s consider 2 input parameters; one for a given name and one for a surname. In a traditional data-driven approach in which the static test data is read in by rows our test data file might be similar to:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Bob,Smith      &lt;br /&gt;John,Johnson       &lt;br /&gt;Roger,Williams       &lt;br /&gt;Steve,Abbot&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This static data file would give us 4 sets of test data, but each time the test data is read into the test case the given and surnames are always the same. &lt;/p&gt;  &lt;p&gt;However, if we read in the given names and surnames into 2 collections, and then randomly select a given name and surname from the appropriate collection to pass to the respective parameter we effectively have 16 possible combinations of static test data to work with. An advantage of this approach is that our ‘collections’ of given names and surnames can contain differing numbers of elements (in which case the number of possible combinations of test data is the Cartesian product of the number of elements in each collection).&lt;/p&gt;  &lt;p&gt;Of course there are many ways to accomplish this. For example, one approach is to continue to use a comma or tab-delimited file format and list given names in one row and surnames in a second row. Another approach is to list the given names and surnames in columns in a spreadsheet and read in each column into a collection of some sort. The latter is the approach I used in developing my &lt;a href="http://www.testingmentor.com/tools/tool_pages/pseudoname.htm" target="_blank"&gt;PseudoName&lt;/a&gt; test data generator tool. I chose this approach for 2 reasons; first an Excel spreadsheet is a simple yet powerful file format for storing static test data, and secondly because lists of test data are sometimes better represented in columns rather than rows. &lt;/p&gt;  &lt;p&gt;The following code shows one way to read in test data by columns from an Excel spreadsheet. &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:28b84334-edb0-4964-8bb7-ec5dc6c50df7" class="wlWriterEditableSmartContent"&gt; &lt;div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"&gt; &lt;div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px"&gt;Code Snippet&lt;/div&gt; &lt;div style="background: #ddd; max-height: 300px; overflow: auto"&gt; &lt;ol style="background: #ffffff; margin: 0 0 0 2.5em; padding: 0 0 0 5px; white-space: nowrap"&gt; &lt;li&gt; &lt;span style="color:#008000"&gt;// &amp;lt;copyright file="datareader.cs" company="TestingMentor"&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&lt;span style="color:#008000"&gt;// Copyright &amp;#169; 2009 by Bj Rollison. All rights reserved.&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&lt;span style="color:#008000"&gt;// &amp;lt;/copyright&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&lt;span style="color:#0000ff"&gt;namespace&lt;/span&gt; TestingMentor.TestTool&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; System;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; System.Collections;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; System.Globalization;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; System.Runtime.InteropServices;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; System.Threading;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;using&lt;/span&gt; Excel = Microsoft.Office.Interop.Excel;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; This class contains methods for reading test data from Excel spreadsheets&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff"&gt;class&lt;/span&gt; &lt;span style="color:#2b91af"&gt;TestDataReader&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; This method reads all the data elements in the specified number of&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; columns in the specified Excel spreadsheet containing the test data&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; and copies the data into a multi-dimensional array&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;param name="dataFileName"&amp;gt;&lt;/span&gt;&lt;span style="color:#008000"&gt;The filename containing the test data&lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;param name="columnCount"&amp;gt;&lt;/span&gt;&lt;span style="color:#008000"&gt;The number of columns in the Excel&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; spreadsheet to read&lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;returns&amp;gt;&lt;/span&gt;&lt;span style="color:#008000"&gt;A multi-dimensional array containing the data eleements for&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#808080"&gt;///&lt;/span&gt;&lt;span style="color:#008000"&gt; each column &lt;/span&gt;&lt;span style="color:#808080"&gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff"&gt;string&lt;/span&gt;[][] ExcelColumnReader(&lt;span style="color:#0000ff"&gt;string&lt;/span&gt; dataFileName, &lt;span style="color:#0000ff"&gt;uint&lt;/span&gt; columnCount)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;CultureInfo&lt;/span&gt; originalCulture = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Excel.&lt;span style="color:#2b91af"&gt;Application&lt;/span&gt; excelApp = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Excel.&lt;span style="color:#2b91af"&gt;Workbook&lt;/span&gt; excelWorkbook = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Excel.&lt;span style="color:#2b91af"&gt;Worksheet&lt;/span&gt; excelActiveWorksheet = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;[][] testData = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; &lt;span style="color:#0000ff"&gt;string&lt;/span&gt;[columnCount][];&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;originalCulture = &lt;span style="color:#2b91af"&gt;Thread&lt;/span&gt;.CurrentThread.CurrentCulture;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;Thread&lt;/span&gt;.CurrentThread.CurrentCulture = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; &lt;span style="color:#2b91af"&gt;CultureInfo&lt;/span&gt;(&lt;span style="color:#a31515"&gt;"en-US"&lt;/span&gt;);&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelApp = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; Excel.&lt;span style="color:#2b91af"&gt;Application&lt;/span&gt;();&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelWorkbook = excelApp.Workbooks.Open(&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataFileName,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;String&lt;/span&gt;.Empty,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;String&lt;/span&gt;.Empty,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;Type&lt;/span&gt;.Missing,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;String&lt;/span&gt;.Empty,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;true&lt;/span&gt;,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;true&lt;/span&gt;,&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;,&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;);&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelActiveWorksheet = (Excel.&lt;span style="color:#2b91af"&gt;Worksheet&lt;/span&gt;)excelWorkbook.ActiveSheet;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;for&lt;/span&gt; (&lt;span style="color:#0000ff"&gt;int&lt;/span&gt; i = 0; i &amp;lt; columnCount; i++)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#008000"&gt;// Start at column 1&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;object&lt;/span&gt; columnIndex = i + 1;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#008000"&gt;// Row 1 is the column title; test data starts on Row 2&lt;/span&gt;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;object&lt;/span&gt; rowIndex = 2;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;ArrayList&lt;/span&gt; tempCollection = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; &lt;span style="color:#2b91af"&gt;ArrayList&lt;/span&gt;();&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;while&lt;/span&gt; (&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;((Excel.&lt;span style="color:#2b91af"&gt;Range&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelActiveWorksheet.Cells[rowIndex, columnIndex]).Value2 != &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tempCollection.Add(&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;((Excel.&lt;span style="color:#2b91af"&gt;Range&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelActiveWorksheet.Cells[rowIndex, columnIndex]).Value2);&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rowIndex = (&lt;span style="color:#0000ff"&gt;int&lt;/span&gt;)rowIndex + 1;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;testData[i] = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; &lt;span style="color:#0000ff"&gt;string&lt;/span&gt;[tempCollection.Count];&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;testData[i] = (&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;[])tempCollection.ToArray(&lt;span style="color:#0000ff"&gt;typeof&lt;/span&gt;(&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;));&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#008000"&gt;// Clean up &lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelWorkbook.Close(&lt;span style="color:#0000ff"&gt;false&lt;/span&gt;, &lt;span style="color:#2b91af"&gt;Type&lt;/span&gt;.Missing, &lt;span style="color:#2b91af"&gt;Type&lt;/span&gt;.Missing);&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelWorkbook = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelApp.Quit();&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;excelApp = &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#008000"&gt;// Garbage collection is not pretty, but necessary to release Excel proc&lt;/span&gt;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.&lt;span style="color:#2b91af"&gt;GC&lt;/span&gt;.Collect();&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.&lt;span style="color:#2b91af"&gt;GC&lt;/span&gt;.WaitForPendingFinalizers();&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;if&lt;/span&gt; (originalCulture != &lt;span style="color:#0000ff"&gt;null&lt;/span&gt;)&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#2b91af"&gt;Thread&lt;/span&gt;.CurrentThread.CurrentCulture = originalCulture;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt; &lt;li&gt;&amp;nbsp;&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:#0000ff"&gt;return&lt;/span&gt; testData;&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt; &lt;li style="background: #f3f3f3"&gt;&amp;nbsp;&amp;nbsp;}&lt;/li&gt; &lt;li&gt;}&lt;/li&gt; &lt;/ol&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;  &lt;p&gt;I must tell you that performance can be an issue especially if the columns contain a lot of data. For example, to read in approximately 700 elements of test data in 3 separate columns took slightly less than 1 second, and reading in 1800 elements in 3 columns required just over 4 seconds. Unfortunately, I didn’t compare total byte counts, but it is pretty obvious the greater the number of test data elements being read the longer the read operation will take and you certainly will have to take the read time into consideration in your automated test case.&lt;/p&gt;  &lt;p&gt;Reading static test data line by line from a data file while looping through a data-driven automated test case is a useful test design approach in some situations, this is another useful approach that will allow the test designer to randomize the combinations of static test data values applied to multiple input parameters in multiple iterations of an automated test case.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9905827" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Automation/default.aspx">Test Automation</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category></item><item><title>Measuring Test Automation ROI</title><link>http://blogs.msdn.com/imtesty/archive/2009/08/25/measuring-test-automation-roi.aspx</link><pubDate>Tue, 25 Aug 2009 09:40:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9883257</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9883257.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9883257</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9883257</wfw:comment><description>&lt;P&gt;I just finished reading Implementing Automated Software Testing by E.Dustin, T. Garrett, and B. Gauf and overall this is a good read providing some well thought out arguments for beginning an automation project, and provides strategic perspectives to manage a test automation project. The first chapter made several excellent points such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Automated software testing “&lt;STRONG&gt;is software development&lt;/STRONG&gt;.”&lt;/LI&gt;
&lt;LI&gt;Automated software testing “and manual testing are intertwined and &lt;STRONG&gt;complement &lt;/STRONG&gt;each other.”&lt;/LI&gt;
&lt;LI&gt;And, “The overall objective of AST (automated software testing) is to design, develop, and deliver an automated test and retest capability that&lt;STRONG&gt; increases testing efficiencies&lt;/STRONG&gt;.”&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Of course, I was also pleased to read the section on test data generation since I design and develop&lt;A href="http://www.testingmentor.com/tools/testdatagenerators.htm" target=_blank mce_href="http://www.testingmentor.com/tools/testdatagenerators.htm"&gt; test data generation tools&lt;/A&gt; as a hobby. The authors correctly note that random test data increases flexibility, improve functional testing, and reduce limited in scope and error prone manually produced test data.&lt;/P&gt;
&lt;P&gt;There is also a chapter on presenting the business case for an automation project by calculating a return on investment (ROI) measure via various worksheets. I have 2 essential problems with ROI calculations within the context of test automation. First, if the business manager doesn’t understand the value of automation within a complex software project (especially one which will have multiple iterations) they should read a book on managing software development projects. I really think most managers understand that test automation would benefit their business (in most cases). I suspect many managers have experienced less than successful automation projects but don’t understand how to establish a more successful automation effort. I also suspect really bright business managers are not overly impressed with magic beans. &lt;/P&gt;
&lt;P&gt;Magic beans pimped by a zealous huckster are the second essential problem with automation ROI calculations. Let’s be honest, the numbers produced by these worksheets or other automation ROI calculators are simply magic beans. Now, why do I make this statement? Because the numbers that are plugged into the calculators or worksheets are &lt;A href="http://www.jargondatabase.com/Jargon.aspx?id=9903" target=_blank mce_href="http://www.jargondatabase.com/Jargon.aspx?id=9903"&gt;ROMA data&lt;/A&gt;. I mean really, how many of us can realistically predict the number of atomic tests for any complex project? Also, do all tests take the same amount of time, or will all tests be executed the same number of iterations? Does it take the same amount of time to develop all automated tests, and how does one go about predicting a realistic time for all automated tests to run? And of course, how many of those tests will be automated? (Actually, that answer is easy….the number of automated tests should be 100% of the tests that should be automated.)&lt;/P&gt;
&lt;P&gt;Personally, I think test managers should not waste their time trying to convince their business manager of the value of a test automation project; especially with magic beans produced from ROMA data. Instead test managers should start helping their team members think about ROI at the test level itself. In other words, teach your team how to make smart decisions about what tests to automate and what tests should not be automated because they can be more effectively tested via other approaches.&lt;/P&gt;
&lt;P&gt;In my next post I will outline some factors that testers, and test managers can use to help decide which tests you might consider automating. Basically, the bottom line here is that an automated test should provide significant value to the tester and the organization, and should help free up the testers time in order to increase the breadth and/or scope of testing.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9883257" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Automation/default.aspx">Test Automation</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Management/default.aspx">Test Management</category></item><item><title>Testing is Sampling</title><link>http://blogs.msdn.com/imtesty/archive/2009/07/16/testing-is-sampling.aspx</link><pubDate>Thu, 16 Jul 2009 09:11:21 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9835160</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9835160.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9835160</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9835160</wfw:comment><description>&lt;p&gt;It seems it is about this time of year that I need to detach a bit from the world to reflect back on the past year and reevaluate my personal and professional goals moving forward. Perhaps I am just getting older or perhaps just a bit wiser (that is synonymous with 'sapient' for the C-D crowd), but I find it refreshing to break away this time of year to tend to my gardens, work on my boat, read some novels, and contemplate life's joys. Now, the major work projects are (almost) finished on my boat, the garden is planted and we are harvesting the early produce, and I reset both personal and professional development objectives for the next year and beyond. So, let me get back to sharing some of my ideas about testing.&lt;/p&gt;  &lt;p&gt;Many of you who read this blog also know of my website &lt;a href="http://www.TestingMentor.com"&gt;Testing Mentor&lt;/a&gt; where I post a few job aids and random test data generation tools I've created. I am a big proponent of random test data using an approach I refer to as &lt;em&gt;&lt;strong&gt;probabilistic stochastic test data&lt;/strong&gt;&lt;/em&gt;.&amp;#160; In May I was in Dusseldorf, Germany at the Software &amp;amp; Systems Quality Conference to present a talk on my approach. I especially enjoy these &lt;a href="http://www.sqs-conferences.com/index.htm" target="_blank"&gt;SQS conferences&lt;/a&gt; (now igniteQ) because the attendees are a mix of industry experts and academia, and I was looking for feedback on my approach. I call my approach probabilistic stochastic test generation because the process is a bit more complex than simple random data generation. Similar to random data generation we cannot absolutely predict a &lt;em&gt;probabilistic&lt;/em&gt; system, but we can control the feasibility of specified behaviors. And the adjective &lt;em&gt;stochastic&lt;/em&gt; simply means &amp;quot;pertaining to a process involving a randomly determined sequence of observations each of which is considered as a sample of one element from a probability distribution.&amp;quot; In a nutshell, my approach involves segregating the population into equivalence partitions, then randomly selects elements from specified parameterized equivalence partitions (which is how we know the probability of specific behaviors), finally the data may be mutated until the test data satisfies the defined fitness criteria. By combining equivalence partitioning and basic evolutionary computation (EA) concepts it is possible to generate large amounts of random test data that is representative from a virtually infinite population of possible data.&lt;/p&gt;  &lt;p&gt;One of the questions that came up during the presentation was how many random samples are required for confidence in any given test case; in other words how to we determine the number of tests using randomly generated test data? This is not an easy question to answer because the sample size of any given population depends on several factors such as:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;variability of data &lt;/li&gt;    &lt;li&gt;precision of measurement &lt;/li&gt;    &lt;li&gt;population size &lt;/li&gt;    &lt;li&gt;risk factors &lt;/li&gt;    &lt;li&gt;allowable sampling error &lt;/li&gt;    &lt;li&gt;purpose of experiment or test &lt;/li&gt;    &lt;li&gt;probability of selecting &amp;quot;bad&amp;quot; or uninteresting data &lt;/li&gt; &lt;/ul&gt;  &lt;h4&gt;&lt;strong&gt;Using sampling for equivalence class partition testing&lt;/strong&gt;&lt;/h4&gt;  &lt;p&gt;But, the question also brought to mind a parallel discussion regarding how we go about selecting elements from equivalence class partition subsets. I am adamantly opposed to hard-coding test data in a test case (automated or manual), but a colleague challenged me and said that since any element in an equivalent partition is representative of all elements in that partition then why can't we simple choose a few values from that equivalence subset. I realize this approach is done all the time by many testers; which is perhaps why we sometimes miss problems. But, hard-coding some small subset of values from a relatively large population of possible values is rarely a good idea, and is generally not the most effective approach for robust test design. One problem with hard-coding a variable is that the hard-coded value becomes static, and we know that static test data loses its effectiveness over time in subsequent tests using the same exact test data. Also, by hard-coding specific values in range of values means that we have absolutely 0% probability of including any other values in that range that are not specified. Another problem with hard-coded values stems from the selection criteria used to choose the values from a set of possible values. Typically we select values from a set based on based historical failure indicators, customer data, and our own biased judgment or intuition of ‘interesting’ values. &lt;/p&gt;  &lt;p&gt;However, the problem is that any equivalence class partition is a hypothesis that all elements are equal. Of course, the only way to validate or affirm that hypothesis is to test the entire population of the given equivalence class partition. Using customer-like values, or values based on failure indicators, and especially values we select based on our intuition are biased samples of the population, and may only represent a small portion of the entire population. Also, the number of values selected from any given equivalence partition set is usually fewer than the number required for some reasonable level of statistical confidence. So, while we definitely want to include values representative of our customers, values derived from historical failure indicators, and even our own intuition, we should also apply scientific sampling methods and include unbiased, randomly sampled values or elements from our set of values or population to help reduce uncertainty and increase confidence.&lt;/p&gt;  &lt;p&gt;For example, lets say that we are testing font size in Microsoft Word. Most font sizes range from 1pt through 1638pt and include half-sized fonts as well within that range. That is a population size of 3273 possible values. If we suspected that any value in the population had an equal probability of causing an error the standard deviation would be 50%. In this example, we would need a sample size of 343 statistically unbiased randomly selected values from the population to assert a 95% confidence level with a sampling error or precision of ±5%. Even in this situation, the number of values may appear to be quite large if the tests are manually executed which is perhaps one reason why extremely small subsets of hard-coded values fail to find problems that are exposed by other values within that equivalent partition (all too often after the software is released). Fortunately, statistical sampling is much easier and less costly with automated test cases and probabilistic random test data generation.&lt;/p&gt;  &lt;h4&gt;&lt;strong&gt;Testing is Sampling&lt;/strong&gt;&lt;/h4&gt;  &lt;p&gt;Statistical sampling is commonly used for experimentation in natural sciences as well as studies in social sciences (where I first learned it while studying sociology an anthropology). And, if we really stop to think about it; any testing effort is simply a sample of tests of the virtually impossible infinite population of possible tests. Of course, there is always the probability that sampling misses or overlooks something interesting. But, this is true of any approach to testing and explained by B. Beizer's Pesticide Paradox. The question we must ask ourselves is will statistical sampling of values in equivalence partitions or other test data help improve my confidence when used in conjunction with customer representative data, historical data, and data we intuit based on experience and knowledge?&amp;#160; Will scientifically quantified empirical evidence help increase the confidence of the decision makers?&lt;/p&gt;  &lt;p&gt;In my opinion anything that helps improve confidence and provides empirical evidence is valuable, and statistical sampling is a tool we should understand put into our professional testing toolbox. There are several well established formulas for calculating sample size that can help us establish a baseline for a desired confidence level. But, rather than belabor you with formulas, I decided to whip together a Statistical Sample Size Calculator that I posted to &lt;a href="http://ssscalculator.codeplex.com/" target="_blank"&gt;CodePlex&lt;/a&gt; and also on my &lt;a href="http://www.TestingMentor.com" target="_blank"&gt;Testing Mentor&lt;/a&gt; site to help testers determine the minimum number of samples of statistically unbiased randomly generated test data from a given equivalence partition to use in a test case to help establish a statistically reliable level of confidence. &lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;strong&gt;Cockamamie chaos causes confusion; controlled chaos cultivates confidence!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9835160" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Tools/default.aspx">Test Tools</category></item><item><title>Better Bug Reports</title><link>http://blogs.msdn.com/imtesty/archive/2009/05/20/better-bug-reports.aspx</link><pubDate>Wed, 20 May 2009 20:58:52 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9632607</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9632607.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9632607</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9632607</wfw:comment><description>&lt;p&gt;When we report a bug our hope is that bug is fixed. But, of course we know that isn’t always the case which is why there are usually several alternative resolutions developers, project managers, or managers may choose for resolving a bug such as postponed, won’t fix, and by design. It is unfortunately quite common to see a tester metaphorically explode into passionate fits of outrage when one of their bugs is resolved as postponed, won’t fix, or by design. It is unfortunate because these tantrums often involve the tester hurling personal insults (e.g. “How can the developer be so stupid not to fix this bug&amp;quot;?”), decrying product quality (e.g. “If we don’t fix this bug this product will totally suck!”), and playing the whiny customer card (e.g. “We will loose customers if we don’t fix this bug.”). Yes, in my early years I was also guilty of these sorts of irrational outbursts of hyperbole when a bug that I thought was important was resolved not fixed. But, of course, I quickly learned that such sophistical speculations rarely resulted in the bug being fixed, and mostly lessened my credibility with developers and managers.&lt;/p&gt;  &lt;p&gt;The other day I was speaking with a tester who was a bit miffed because the developer had resolved a few of her bugs as by design and won’t fix and she asked how she could ‘fight’ these resolutions. “Well,” I began, “Getting people to change their minds usually involves negotiation and the logical presentation of facts in a non-judgmental approach. Sometimes you will succeed, and sometimes you will not succeed. As testers surely we want all our bugs to be fixed; however, from a practical standpoint that may not always be the case especially if the bug is subjective.” I previously wrote about &lt;a href="http://blogs.msdn.com/imtesty/archive/2006/06/28/649862.aspx" target="_blank"&gt;10 common problems with bug reporting&lt;/a&gt;, but, in this case I proceeded to discuss a few strategies I use to advocate bugs.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Make it easy for the developer to fix the bug&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;As a minimum a tester must provide a description of the problem, the environmental conditions in which the problem occurred (if localized to a specific environment), the shortest number of exact steps to reproduce the bug, and the actual results versus the expected results. Occasionally a screen shot may be beneficial, but mostly if there is a contrasting example. But, I will also point the developer to my test; especially if it is automated. Providing the developer an automated mechanism to reproduce a problem reduces a lot of overhead. Of course, in this case I am talking about an automated test case that runs in a few seconds, or an automated script that even assists the developer reproduce the problem quickly.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Provide specific contradictions to specified and/or implied requirements or standards&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Of course, if the product design or functionality deviates from stated requirements pointing this out in a non-confrontational way is a no-brainer. The key here is our argument must be non-confrontational because sometimes we may misinterpret the requirements, and sometimes the requirements may change without us being aware of those changes. There are also occasionally deviations from implied requirements such a UI design guidelines as a result of the introduction of new technologies, or changes in how customers use the product based on usability studies. Other implied standards include competing products or previous versions of the product. In any case, when arguing for a bug fix based on specified or implied requirements I recommend using a compare and contrast type of approach to better illustrate the problem as I perceive it.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Provide concrete examples of customer impact&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;This is really important! Providing a real world scenario that clearly illustrates not only how this bug will manifest itself to the customer, but also providing corroborating evidence from customers presents a strong case in favor of a bug fix. There are several useful repositories of customer feedback testers can use to bolster their point of view such as newsgroups, popular blogs, trade journal reviews of past or similar products, at Microsoft we also have Watson and SQM data, and product support reports. Using ‘real-world’ constructive feedback is often more meaningful than an internal mutiny by a portion of the test team.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Know your primary target customer profile&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Testers often like to think we are representative of our customers. However, this may not always be the case. (It has always puzzled me as to why testers seem to think they have some greater affinity to the end user customer as compared to others on the product team.) Yes, it is important that testers understand who the primary target customer is for the current project or release and that is why many teams have detailed personas of primary, secondary, and sometimes even tertiary customer audiences. Of course, if we are in the commercial software business we want our customer base to be as large as possible. But, as the number of customers increase so does the diversity of value, and as they say…you can never please everyone! So, when defending your position to fix a particular bug it is always better to frame the discussion from the point of view of the primary customer persona as compared to your own personal bias.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Use your brain, not your emotions&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Passion has long been an admired trait in software testers. However, unbridled passion fraught with antagonistic accusations can be detrimental to a successful bug resolution (and sometimes even a career). Some bugs obviously need to be fixed, while others may be more dependent on several mitigating (and competing) factors such as where you are in the software lifecycle, business impact, primary customer impact, risk, etc. I think it is largely agreed that perhaps the primary role of testers is to provide information, but that means we must also gather the pertinent information and represent that information logically within the relevant context to the management team (or decision makers). Remember…reckless rants rarely render reasonable results! &lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9632607" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item><item><title>Programming Paradigms in Test Automation</title><link>http://blogs.msdn.com/imtesty/archive/2009/05/14/programming-paradigms-in-test-automation.aspx</link><pubDate>Thu, 14 May 2009 06:26:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9613544</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9613544.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9613544</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9613544</wfw:comment><description>&lt;P&gt;Regardless of the personal opinions of a few people, the simple fact is that the demand for software testers who can design and develop effective test automation is increasing. Perhaps one reason for the distain by some folks in the industry is due to limitations of the test automation approach they are most familiar with, and they sometimes assume those limitations apply to all types of test automation. However, not all test automation approaches are equal, and there are advantages and disadvantages for any approach.&lt;/P&gt;
&lt;P&gt;At its core an automated test case is software code, and similar to the various approaches used in developing product software there are different programming paradigms used to develop test automation such as:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Record and playback automation &lt;/LI&gt;
&lt;LI&gt;Keyword or action-word driven automation &lt;/LI&gt;
&lt;LI&gt;Scripted automation &lt;/LI&gt;
&lt;LI&gt;Procedural automation &lt;/LI&gt;
&lt;LI&gt;Model based automation &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&amp;nbsp;&lt;STRONG&gt;Record and playback automation&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The record and playback&amp;nbsp;paradigm simply records sequences of keyboard and mouse events, auto-magically codifies them usually into some proprietary scripting language which can then be replayed (executed) over and over again. There are usually severe limitations to this type of automation and it tends to be extremely fragile requiring constant massaging (re-recording). Although many record/playback&amp;nbsp;paradigm allows 'test developers' to modify the scripted actions to some extent, and possibly even incorporate simple yes/no oracles I think many people view the record/playback&amp;nbsp;paradigm as being slightly more useful than trained monkeys in limited situations.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Keyword or action-word driven automation&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Keyword or action-words are simple scripts usually in some tabular format that 'describe' a sequence of 'actions' for the computer to perform. Of course, the key to keywords is the underlying architecture of the tool that interprets the keywords and executes the sequence of events. The beauty of keyword driven testing is that it hides the actual code, and similar to record and playback can be more easily used by business analysts or 'user domain experts' hired to into testing roles to automate something. I do see the benefit of keyword driven testing in some limited contexts (especially for companies who rely on business analysts/user domain experts for testing software), but let's be real…these people aren't automating anything…they are simply filling out a form that is then feed into a tool that performs the actions as prescribed by the listed instructions. The keyword form does nothing by itself, and the only thing a 'tester' has to think about is using the correct key words to sequentially get from point A to point Z for a 'test.' &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Scripted automation (imperative programming)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The primary difference between keyword and scripted automation is the tester actually develops the test in a programming language rather than filling in a form with abstracted key words that drive some automation engine. However, similar to keywords, scripted automation tends to use rudimentary statements of basic instructions that manipulate the software to perform a pre-determined sequence of events as illustrated below.&lt;/P&gt;
&lt;DIV style="BORDER-BOTTOM: gray 1px solid; BORDER-LEFT: gray 1px solid; PADDING-BOTTOM: 4px; LINE-HEIGHT: 12pt; BACKGROUND-COLOR: #f4f4f4; MARGIN: 20px 0px 10px; PADDING-LEFT: 4px; WIDTH: 97.5%; PADDING-RIGHT: 4px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; MAX-HEIGHT: 200px; FONT-SIZE: 8pt; OVERFLOW: auto; BORDER-TOP: gray 1px solid; CURSOR: text; BORDER-RIGHT: gray 1px solid; PADDING-TOP: 4px"&gt;
&lt;DIV style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   1:&lt;/SPAN&gt; def test_b_googlenews&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   2:&lt;/SPAN&gt;   &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   3:&lt;/SPAN&gt;   #-------------------------------------------------------------------------&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   4:&lt;/SPAN&gt;   # Test to demonstrate WATIR select from drop-down box functionality&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   5:&lt;/SPAN&gt;   #&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   6:&lt;/SPAN&gt;   &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   7:&lt;/SPAN&gt;   #variables&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   8:&lt;/SPAN&gt;   test_site = &lt;SPAN style="COLOR: #006080"&gt;'http://news.google.com'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   9:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  10:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'## Beginning of test: google news use drop-down box'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  11:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'  '&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  12:&lt;/SPAN&gt;  &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  13:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'Step 1: go to the google news site: news.google.com'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  14:&lt;/SPAN&gt;   $browser.&lt;SPAN style="COLOR: #0000ff"&gt;goto&lt;/SPAN&gt;(test_site)&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  15:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'  Action: entered '&lt;/SPAN&gt; + test_site + &lt;SPAN style="COLOR: #006080"&gt;' in the address bar.'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  16:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  17:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'Step 2: Select Canada from the Top Stories drop-down list'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  18:&lt;/SPAN&gt;   $browser.select_list( :index , 1).select(&lt;SPAN style="COLOR: #006080"&gt;"Canada English"&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  19:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'  Action: selected Canada from the drop-down list.'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  20:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  21:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'Step 3: click the "Go" button'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  22:&lt;/SPAN&gt;   $browser.button(:caption, &lt;SPAN style="COLOR: #006080"&gt;"Go"&lt;/SPAN&gt;).click&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  23:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'  Action: clicked the Go button.'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  24:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  25:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'Expected Result: '&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  26:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;' - The Google News Canada site should be displayed'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  27:&lt;/SPAN&gt;  &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  28:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'Actual Result: Check that "Canada" appears on the page by using an assertion'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  29:&lt;/SPAN&gt;   assert($browser.text.include?(&lt;SPAN style="COLOR: #006080"&gt;"Canada"&lt;/SPAN&gt;) )&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  30:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  31:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'  '&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  32:&lt;/SPAN&gt;   puts &lt;SPAN style="COLOR: #006080"&gt;'## End of test: google news selection'&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  33:&lt;/SPAN&gt;  &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  34:&lt;/SPAN&gt; end # end of test_googlenews&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  35:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  36:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  37:&lt;/SPAN&gt; def test_c_googleradio&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  38:&lt;/SPAN&gt;   &lt;/PRE&gt;&lt;/DIV&gt;&lt;/DIV&gt;
&lt;P&gt;Most examples of scripted automation appear as codified versions of a set of steps listed in a less-than-adequately designed manual test case using hard-coded arguments for variables, mindless progression between steps, and simple deterministic oracles. Scripted automation is probably most beneficial for automating specific sub-tasks in "computer assisted testing." However, scripted automation is usually too prescriptive, and rely heavily on nothing going wrong during the execution of the test case.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Procedural automation (procedural programming)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;In procedural automation the testers also develops a test by writing a&amp;nbsp; series of computational steps to achieve a desired purpose. However unlike scripted automation the procedural automation paradigm generally provides better control flow options during the execution of the automated test case, allows for greater complexity in the design, improves reuse and reduces maintenance through modularity, and can employ both deterministic and heuristic oracles. &lt;/P&gt;
&lt;DIV style="BORDER-BOTTOM: gray 1px solid; BORDER-LEFT: gray 1px solid; PADDING-BOTTOM: 4px; LINE-HEIGHT: 12pt; BACKGROUND-COLOR: #f4f4f4; MARGIN: 20px 0px 10px; PADDING-LEFT: 4px; WIDTH: 97.5%; PADDING-RIGHT: 4px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; MAX-HEIGHT: 200px; FONT-SIZE: 8pt; OVERFLOW: auto; BORDER-TOP: gray 1px solid; CURSOR: text; BORDER-RIGHT: gray 1px solid; PADDING-TOP: 4px"&gt;
&lt;DIV style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   1:&lt;/SPAN&gt; &lt;SPAN style="COLOR: #008000"&gt;// Procedural programming example &lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   2:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   3:&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; Main(&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;[] args)&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   4:&lt;/SPAN&gt; {&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   5:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; logResult = &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.Empty;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   6:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   7:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #008000"&gt;// Path to the data file passed as a string argument to the test case&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   8:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; pictTestData = args[0];&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;   9:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  10:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #008000"&gt;//Stopwatch to measure test case duration&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  11:&lt;/SPAN&gt;   Stopwatch sw = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Stopwatch();&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  12:&lt;/SPAN&gt;   sw.Start();&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  13:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  14:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #008000"&gt;// Launch the AUT&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  15:&lt;/SPAN&gt;   AutomationElement desktop = AutomationElement.RootElement;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  16:&lt;/SPAN&gt;   AutomationElement myAutForm = &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  17:&lt;/SPAN&gt;   Process myProc = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Process();&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  18:&lt;/SPAN&gt;   myProc.StartInfo.FileName = myConstantAutFileName;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  19:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (myProc.Start())&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  20:&lt;/SPAN&gt;   {&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  21:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// Polling loop to find AUT window by window property&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  22:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; pollCount = 0;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  23:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;do&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  24:&lt;/SPAN&gt;     {&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  25:&lt;/SPAN&gt;       myAutForm = desktop.FindFirst(TreeScope.Children,&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  26:&lt;/SPAN&gt;         &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; PropertyCondition(AutomationElement.AutomationIdProperty,&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  27:&lt;/SPAN&gt;         myConstantAUTPropertyID));&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  28:&lt;/SPAN&gt;       pollCount++;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  29:&lt;/SPAN&gt;       System.Threading.Thread.Sleep(100);&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  30:&lt;/SPAN&gt;     }&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  31:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;while&lt;/SPAN&gt; (myAutForm == &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt; &amp;amp;&amp;amp; pollCount &amp;lt; 50);&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  32:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  33:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (myAutForm == &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  34:&lt;/SPAN&gt;     {&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  35:&lt;/SPAN&gt;       &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Exception(&lt;SPAN style="COLOR: #006080"&gt;"Failed to find dialog"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  36:&lt;/SPAN&gt;     }&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  37:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  38:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// Get UI element collection here...&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  39:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  40:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// Call method to read in test data&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  41:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;[] testData = ReadTabDelimitedFile(pictTestData);&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  42:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  43:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// iterate through each set of test data (data-driven test example)&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  44:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #0000ff"&gt;foreach&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; test &lt;SPAN style="COLOR: #0000ff"&gt;in&lt;/SPAN&gt; testData)&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  45:&lt;/SPAN&gt;     {&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  46:&lt;/SPAN&gt;       &lt;SPAN style="COLOR: #008000"&gt;// Call method to execute each set of test data and assign the return&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  47:&lt;/SPAN&gt;       &lt;SPAN style="COLOR: #008000"&gt;// value to the logResult variable; Oracle is separate method called &lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  48:&lt;/SPAN&gt;       &lt;SPAN style="COLOR: #008000"&gt;// from the test method&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  49:&lt;/SPAN&gt;       LogResultMethod(ExecuteCombinatorialTestMethod(test));&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  50:&lt;/SPAN&gt;     }&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  51:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  52:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// close AUT and clean-up&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  53:&lt;/SPAN&gt;     TimeSpan ts1 = sw.Elapsed;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  54:&lt;/SPAN&gt;     &lt;SPAN style="COLOR: #008000"&gt;// log test case duration...&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  55:&lt;/SPAN&gt;&amp;nbsp; &lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: #f4f4f4; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  56:&lt;/SPAN&gt;   &lt;SPAN style="COLOR: #008000"&gt;// Deal with situation if AUT failed to launch&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BORDER-BOTTOM-STYLE: none; PADDING-BOTTOM: 0px; LINE-HEIGHT: 12pt; BORDER-RIGHT-STYLE: none; BACKGROUND-COLOR: white; MARGIN: 0em; PADDING-LEFT: 0px; WIDTH: 100%; PADDING-RIGHT: 0px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BORDER-TOP-STYLE: none; COLOR: black; FONT-SIZE: 8pt; BORDER-LEFT-STYLE: none; OVERFLOW: visible; PADDING-TOP: 0px"&gt;&lt;SPAN style="COLOR: #606060"&gt;  57:&lt;/SPAN&gt; }&lt;/PRE&gt;&lt;/DIV&gt;&lt;/DIV&gt;
&lt;P&gt;Procedural automation can be used for anything from API to GUI automated test cases designed to evaluate functionality (computational logic), non-functional areas such as stress, performance, and security, and also behavioral&amp;nbsp; . Using a language similar to the programming language removes abstraction layers, and also enables other members of the team (developers) to easily review test cases.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Model based automation&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Model based automation is a relatively new automation paradigm, and its complexity is beyond the scope of this single post. Basically, model based automation involves codifying abstracted state machines and state traversals and couples these parts with an automation engine that uses some form of graph traversal logic to drive the system under test between the various state machines identified in the model. In some sense model based automation is similar to exploratory testing because tests are generally not pre-determined or pre-scripted, what constitutes a single test is really hard to describe, and the oracles generally detect errant behavior (or being in an unexpected state). Personally, I think there is tremendous potential in model based automation, but the industry has just begun to scratch the surface of this automation paradigm and it is still largely misunderstood. This automation paradigm requires more complex skill sets of the person designing the test automation such as the ability to abstract important machine states as a model, and encode system behaviors. For more information about model based automation I recommend taking a look at &lt;A href="http://research.microsoft.com/en-us/projects/specexplorer" mce_href="http://research.microsoft.com/en-us/projects/specexplorer"&gt;http://research.microsoft.com/en-us/projects/specexplorer&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;So, approach which is best?&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;In my opinion, there may be some limited value in record/playback, keyword, and scripted automation in specific contexts; however, a robust automated test case that will run on multiple environments, multiple languages, and distributed across multiple platforms without rewriting the test for each variation requires well designed tests developed using procedural automation or model based automation approach. &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9613544" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Automation/default.aspx">Test Automation</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category></item><item><title>Assessing Tester Performance</title><link>http://blogs.msdn.com/imtesty/archive/2009/04/28/assessing-tester-performance.aspx</link><pubDate>Tue, 28 Apr 2009 19:59:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9573845</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9573845.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9573845</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9573845</wfw:comment><description>&lt;P&gt;Using context-free software product measures as personal performance indicators (KPI) is about as silly as &lt;A href="http://en.wikipedia.org/wiki/Pet_rock" mce_href="http://en.wikipedia.org/wiki/Pet_rock"&gt;pet rocks&lt;/A&gt;!&lt;/P&gt;
&lt;P&gt;Periodically a discussion of assessing tester performance surfaces on various discussion groups. Some people offer advice such as counting bugs (or some derivation thereof), number of tests written in x amount of time, number of tests executed, % of automated tests compared to manual tests, and (my one of my least favorite measures of individual performance) % of code coverage. &lt;/P&gt;
&lt;P&gt;The problem with all these measures is they lack context, and tend to ignore dependent variables. It is also highly likely that an astute tester can easily game the system and potentially cause detrimental problems. For example, if my manager considered one measure my performance on the number of bugs found per week, I would ask how many I had to find per week to satisfy the 'expected' criteria. Then each week I would report 2 or 3 more bugs than the 'expected' or 'average' number (in order to 'exceed' expectations), and any additional bugs I found that week, I would sit on and hold in case I was below my quota the following week. Of course, this means that bug reports are being artificially delayed which may negatively impact the overall product schedule. &lt;/P&gt;
&lt;P&gt;The issue at hand is this bizarre desire by some simple-minded people who want an easy solution to a difficult problem. But, there is no simple formula for measuring the performance of an individual. Individual performance assessments are often somewhat subjective, and influenced by external factors identified through &lt;A href="http://www.humanperformancetechnology.org/" target=_blank mce_href="http://www.humanperformancetechnology.org/"&gt;Human Performance Technology (HPT)&lt;/A&gt; research such as motivation, tools, inherent ability, processes, and even the physical environment.&lt;/P&gt;
&lt;P&gt;A common problem I often see is unrealistic goals such as "Find the majority of bugs in my feature area." (How do we know what the majority is? What if the majority doesn't include the most important issues? etc.) Another problem I commonly see is for individuals to over-promise and under-deliver relative to their capabilities. I also see managers who dictate the same identical set of performance goals to all individuals. While there may be a few common goals, as a manager I would want to tap into the potential strengths of each individual on my team. I also have different expectations and levels of contributions from individuals depending on where they are in their career, and also based on their career aspirations.&lt;/P&gt;
&lt;P&gt;So, as testers we must learn to establish &lt;A href="http://www.topachievement.com/smart.html" target=_blank mce_href="http://www.topachievement.com/smart.html"&gt;SMART&lt;/A&gt; goals with our managers that include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;goals that align with my manager's goals&lt;/LI&gt;
&lt;LI&gt;goals that align with the immediate goals of the product team or company&lt;/LI&gt;
&lt;LI&gt;and stretch goals that illustrate continued growth and personal improvement relative to the team, group, or company goals&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;(This last one may be controversial; however, we shouldn't be surprised to know individual performance is never constant in relation to your peer group. )&lt;/P&gt;
&lt;P&gt;But, (fair or not) for a variety of reasons most software companies do (at least periodically) evaluate their employee performance in some manner, the key to success is in HPT and agreeing on SMARTer goals upfront.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9573845" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Management/default.aspx">Test Management</category></item><item><title>"Good enough" is not good enough!</title><link>http://blogs.msdn.com/imtesty/archive/2009/04/17/good-enough-is-not-good-enough.aspx</link><pubDate>Sat, 18 Apr 2009 00:02:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9554623</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9554623.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9554623</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9554623</wfw:comment><description>&lt;P&gt;This week I came across a discussion [regarding test design]&amp;nbsp;in which a tester wrote, "…&lt;EM&gt;the&lt;/EM&gt; &lt;EM&gt;main goal is having something that is 'good enough'&lt;/EM&gt;." Every time I hear a tester utter the phrase "&lt;EM&gt;good enough&lt;/EM&gt;" my head wants to explode! &lt;/P&gt;
&lt;P&gt;Wrapping duct tape around a splint on the broken handle on my hoe is good enough to finish the job until I can go buy a new handle. While I may sometimes temporarily improvise a "good enough" solution; I am never truly satisfied with good enough, and I personally aspire to be better than good enough. My father always told me if something was worth doing, I should do it right! He also raised me to always put forth my best effort, and constantly strive to improve myself. &lt;/P&gt;
&lt;P&gt;I seriously can't think of any professional (in any discipline) who seriously considers good enough to ever really be good enough? The "good enough" argument is the ultimate cop out! In my opinion "good enough" epitomizes an unprofessional, apathetic attitude sanctioning mediocrity.&lt;/P&gt;
&lt;P&gt;From a job performance perspective I suspect that if we told our employers that we were going to simply design and execute tests that are "good enough" we probably wouldn't be in a job very long. I certainly would not want people on my team who are satisfied with "good enough;" I want people who want to do their best, and to strive for better!&lt;/P&gt;
&lt;P&gt;I spent some time in the US Air Force and we often used the phrase "it's good enough for government work" to describe slop-shoddy work. So, it amazes me that some people seem to be satisfied by consciously condoning ignominious practices. But, I guess some people are taught to expend just enough effort to be good enough!&lt;/P&gt;
&lt;P&gt;In my opinion, good enough may be "good enough for government work" or for individuals who don't have a vested interested in helping organizations improve, or who don't really care about improving themselves; but, there is no room for the slovenly "good enough" mentality among professional testers.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9554623" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Disorderly+diatribes+and+denunciations/default.aspx">Disorderly diatribes and denunciations</category></item><item><title>The quality quandary</title><link>http://blogs.msdn.com/imtesty/archive/2009/03/27/the-quality-quandary.aspx</link><pubDate>Fri, 27 Mar 2009 18:27:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9514421</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9514421.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9514421</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9514421</wfw:comment><description>&lt;P&gt;I often find discussions about quality to be hypothetical, and in fact unless you define your specific context the word itself is nebulous, vague, or simply meaningless philosophical psycho-babble. For a while now, I previously &lt;A href="http://blogs.msdn.com/imtesty/archive/2007/07/16/quality-is-not-value.aspx" target=_blank mce_href="http://blogs.msdn.com/imtesty/archive/2007/07/16/quality-is-not-value.aspx"&gt;posted&lt;/A&gt; my opposition to the simplistic notion that quality is value to some person. Sure, most thesaurus' equate the two words as synonyms, and the context-driven posse constantly regurgitate Weinberg's&amp;nbsp; quote about "quality is value to some person." Yes, that is one perspective of quality, but only one.&lt;/P&gt;
&lt;P&gt;In the past I have taught that quality is not value in the context as one of the goals of software testing. My definition of value is the purpose or usefulness of a product in satisfying a customer's needs or wants. My definition of quality was based on tangible aspects of the attributes or capabilities of a product from an engineering perspective. Our definition of &lt;EM&gt;&lt;STRONG&gt;software&lt;/STRONG&gt;&lt;/EM&gt; testing as any task designed to evaluate or assess the attributes and capabilities of a software project in relation to implicit or explicit guidelines in order to provide information to the management team (the people who make the business decisions). Those evaluations result in measurements we call quality measurements or criteria (the "&lt;EM&gt;essential or distinctive characteristics, properties, or attributes&lt;/EM&gt;" that are critical for the success of our project) and that is part of the information we present to the decision makers to help them make more informed business decisions. Remember, Weinberg also stated &lt;EM&gt;"Thinking about measurability from the beginning is an essential part of creating a well-formed effort."&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;The other day I met with members of one of our research teams and they were talking about quality in terms of tenet and non-tenet quality; and I clarified that basically they were essentially talking about the customer perceptions of quality versus the engineering aspects of quality. Surely, from a holistic point the observation and reliable measurements of both perspectives of quality are important for the success of any organization. Customers usually buy/download software because it helps them satisfy some need or want. The decision of which software product to buy made be based on personal bias, or perhaps the herding instinct, or it may be more rational based on the comparison of features (measureable attributes and capabilities). Once the customer begins to use the software they form their own opinions based on expectations and/or previous experiences that provides the company with information regarding customer satisfaction.&lt;/P&gt;
&lt;P&gt;So, the next time someone starts talking about quality stop them and ask them if they are talking about the engineering aspects of quality, or the customer perceptions of quality. They are closely related, but different perspectives of the same topic.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9514421" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Management/default.aspx">Test Management</category></item><item><title>Exploratory testing inside the box</title><link>http://blogs.msdn.com/imtesty/archive/2009/03/20/exploratory-testing-inside-the-box.aspx</link><pubDate>Fri, 20 Mar 2009 11:24:39 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9492020</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9492020.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9492020</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9492020</wfw:comment><description>&lt;p&gt;Much of the information about exploratory testing focuses on testing from an end-user perspective. Pundits of exploratory testing claim the approach is also useful from a white box test design approach, but I have yet to see any practical discussion or examples. But, professional testers use exploratory testing approaches all the time from a white box perspective to explore the code for untested paths. Professional testers learn about areas of the code that are at risk, and reactively design effective tests to evaluate previously untested or under-tested areas of the code.&lt;/p&gt;  &lt;p&gt;Let's use a simple example to get started. Suppose we had to drive from Lynnwood, Washington to Puyallup, Washington without a map or (GPS auto navigation system). Just as we have 'clues' to point us in the various directions while performing exploratory testing at the user interface we have the numerous highway signs to help us navigate various routes to complete our journey. And, it is up to us to decide which route to take. The shortest route is I-405 south to SR-167. But, I-405 is always at a stand-still, so another popular route is I-5 to SR-18 east then SR-167 south. Of course, after traversing those routes a couple of times the scenery (and crawling in traffic) gets a bit boring, so we might find additional less travelled routes. But, regardless of how many times we make the journey or how many different drivers we choose to complete this journey it is highly unlikely that we will traverse every possible route in any reasonable amount of time. Some routes may not be obvious such as I-5 south to Seattle, then taking the ferry from Seattle to Bremerton and continuing to SR-310 south to SR-16, then I-5 north to SR-167. And, of course some routes are impossible (or at least so convoluted they would be improbable).&lt;/p&gt;  &lt;p&gt;&lt;img title="map" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="782" alt="map" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/StructuralExploration_143D6/map_thumb.jpg" width="635" border="0" /&gt; &lt;/p&gt;  &lt;p&gt;Fortunately, control flow through even complex algorithms is not as labyrinthine as the state roadways in western Washington. And, just as the department of transportation uses various tools to measure traffic volumes testers can use path profiling tools to measure frequently traversed paths through the code. We can also use code coverage tools to see what paths have or have not been traversed, and which decisions are made at branching statements. Using code coverage and profiling tools to map control flow through the algorithm we are able to more thoroughly explore the code. Using our 'map' we can learn what paths have not been traversed and even whether or not certain paths through the code are even possible. After we explore the 'map' we can more effectively design additional tests to traverse un-tested paths through an algorithm. Common structural test design techniques include&amp;#160; to evaluate code statements, code blocks, simple decisions or branches, or multiple Boolean conditional clauses in a single predicate statement. Then, using those test designs we can execute those tests either using stubs or mock objects at the unit or component level, or through the user interface to traverse those paths to reduce overall susceptibility to risk.&lt;/p&gt;  &lt;p&gt;I discuss the various techniques commonly used in structural testing in Chapter 6 of our book &lt;em&gt;&lt;a href="http://www.amazon.com/How-We-Test-Software-Microsoft/dp/0735624259" target="_blank"&gt;How We Test Software At Microsoft&lt;/a&gt;&lt;/em&gt;, and also address the subject &lt;a href="http://blogs.msdn.com/imtesty/archive/2007/08/14/code-coverage-is-inversely-proportional-to-the-critical-information-it-provides.aspx" target="_blank"&gt;here&lt;/a&gt;, and &lt;a href="http://blogs.msdn.com/imtesty/archive/2009/03/06/basic-blocks-aren-t-so-basic.aspx" target="_blank"&gt;here&lt;/a&gt;. Of course, the application of structural techniques is usually referred to as code coverage analysis. But, using this simple analogy hopefully other testers can begin to understand how exploratory testing approaches are used not only from the user interface, but also below the GUI at the code level. As Boris Beizer initially stated, &amp;quot;all testing is essentially exploratory in nature,&amp;quot; and code coverage analysis (analyzing code coverage results to learn about, design additional tests, then execute those tests) also makes great use of exploratory approaches inside the box.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9492020" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item><item><title>GUI test automation is not child's play</title><link>http://blogs.msdn.com/imtesty/archive/2009/03/12/gui-test-automation-is-not-child-s-play.aspx</link><pubDate>Fri, 13 Mar 2009 01:29:28 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9472276</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9472276.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9472276</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9472276</wfw:comment><description>&lt;p&gt;There are many approaches to test automation from unit testing to system level testing through the GUI. Of course, the most often discussed approach is the automation approach that drives the GUI to perform some action; or GUI automation. This also happens to be the most controversial approach to test automation, and is perhaps the hardest type of automation to design and develop. One reason why an automated GUI test fails or doesn't achieve its potential is due to a lack of understanding of the &amp;quot;system&amp;quot; by the tester, which in turn leads to a poorly designed test from the outset. &lt;/p&gt;  &lt;p&gt;This problem is especially obvious when people who may have specialized business knowledge but lack a in-depth understanding of the systems they are working on (non-technical testers) are asked to 'automate' something. The automation in this case is usually in the form of using record/playback tools or perhaps creating a rote script to drive a keyword-driven framework, and the 'test' is usually consists of nothing more than merely mimicking some contrived behavior by the 'tester.' In fact, this over-simplified view of test automation is sometimes perpetuated by tool vendors. A manager at one tool vendor said, &amp;quot;By automatically capturing the tester's process and documenting their keystrokes and mouse clicks, the tester will have more time for the interesting, value-add activities, such as testing complex scenarios that would be difficult or impossible to justify automating.&amp;quot; &lt;/p&gt;  &lt;p&gt;There are 2 fundamental problems with the above quote, and this approach to 'automation' (and I use that term loosely in this context). First, while I don't totally discount the value of record/playback, and in the right context it could very well be the best approach in a specific situation, the general consensus in the industry is that due to the limited capabilities of record/playback type tools this type of automation is simply one level above using a hoard of monkeys trained to repeat a set of sequences using the keyboard and mouse. In fact, some might suggest trained monkeys may be a better alternative because bananas are much cheaper than the costs of licensing a tool and then realizing that you have to hire someone to try to patch together some proprietary script in an often vain attempt to build an automation test suite that is beyond the reasonable capabilities of a record/playback automation approach. Unfortunately, in either case the organization is usually left with a horrible mess that nobody wants to clean up. Secondly, it makes a ridiculous assumption that testers are too stupid to automate complex scenarios or test automation is a brain-dead, non-interesting, zero-value-add activity. &lt;/p&gt;  &lt;p&gt;If simply recording or 'documenting' rudimentary scripts that essentially repeat a sequence of 'hard-coded' steps over and over again is someone's idea of well-designed test automation then I would agree that automation is a brain-dead activity. When you automate poorly designed tests, you simply get poorly designed automated tests! And, since I don't mind calling the kettle black, I will say it…recording a set of actions, or documenting a sequence of steps with hard-coded values to feed into a keyword driven architecture is not test automation! Surely it automates tasks, but well designed automated tests are much more powerful than the production of some crude script that automates the actions performed by someone sitting in front of a computer.&lt;/p&gt;  &lt;p&gt;Of course, well designed automated tests require highly skilled professional testers with in-depth knowledge of the systems they are working on, as well as proficiency in programming concepts and languages. Similar to how doctors study anatomy, physiology, pharmacology, immunology, biochemistry, etc., professional testers need to constantly study the various systems they are tasked to test. Developing a well designed automated test is very different than simply using a tool to automatically repeat a sequence of actions. Designing robust tests (automated or not) requires not only incredible creativity and problem-solving skills, but an in-depth knowledge of the system and an understanding of how to manipulate the system programmatically. &lt;/p&gt;  &lt;p&gt;But, if you buy into the idea that automation is simple and merely rote recording or documenting some sequence of steps performed by some person then you get exactly that; simplistic repetitive automated actions. Simple automation is simply automated simplicity.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9472276" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/Test+Automation/default.aspx">Test Automation</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category></item><item><title>Basic Blocks Aren't So Basic</title><link>http://blogs.msdn.com/imtesty/archive/2009/03/06/basic-blocks-aren-t-so-basic.aspx</link><pubDate>Fri, 06 Mar 2009 09:51:08 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9461895</guid><dc:creator>I.M.Testy</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/imtesty/comments/9461895.aspx</comments><wfw:commentRss>http://blogs.msdn.com/imtesty/commentrss.aspx?PostID=9461895</wfw:commentRss><wfw:comment>http://blogs.msdn.com/imtesty/rsscomments.aspx?PostID=9461895</wfw:comment><description>&lt;p&gt;In the book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0735624259/compendiumdev-20" target="_blank"&gt;&lt;em&gt;How We Test Software at Microsoft&lt;/em&gt;&lt;/a&gt; I discuss structural testing techniques. Structural testing techniques are systematic procedures designed to analyze and evaluate control flow through a program. These are classic white box test design techniques, although my friend and respected colleague &lt;a href="http://www.eviltester.com/index.php/about/" target="_blank"&gt;Alan Richardson&lt;/a&gt; states in his review of the book that he also &lt;a href="http://www.compendiumdev.co.uk/blog/index.php/2009/02/04/book-review-how-we-test-software-at-microsoft/" target="_blank"&gt;employs similar techniques on models&lt;/a&gt; and I have to agree with him on that point.&lt;/p&gt;  &lt;p&gt;Also, Peter M. sent me mail pointing out a reasonably obvious bug in the code chunks on pages 118 and 119. Both functions are declared as static void, but each has a return statement. Somehow this oversight made it through the review process, but of course a return statement in a function declared as static void would cause a compiler error. (Thanks for discovering that bug Peter and letting us know so we can fix it for the 2nd edition!) &lt;/p&gt;  &lt;p&gt;Peter also asked for further clarification of how blocks are counted, and why a test that evaluated both conditional clauses in the compound expression as true in the below example (and on page 119) results in 85.71% coverage. Unfortunately, the answer for that is not simple. &lt;/p&gt;  &lt;h1&gt;Some surprising details…&lt;/h1&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; BlockExample1(&lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; cond_1, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; cond_2)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; x = 0, y = 0, z = 0;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (cond_1 &amp;amp;&amp;amp; cond_2)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;   {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     x = 1;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     y = 2;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;     z = 3;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;   }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; x + y + z;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The above code can be re-written as:&lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; height: 250px; background-color: #f4f4f4"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; BlockExample2(&lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; cond_1, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; cond_2)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; x = 0, y = 0, z = 0;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (cond_1)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;   {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (cond_2)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;       x = 1;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;       y = 2;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;       z = 3;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;   }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;   &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; x + y + z;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;First, a 'basic block' is defined as a set of contiguous executable statements with no logical branches which seems pretty straight forward. So, based on our definition of basic blocks it appears there are 4 blocks of contiguous statements. However, the conditional clauses on line 4 and line 6 in the BlockExample2 method introduce logical branches which theoretically introduce 2 implicit blocks (e.g. one block when control flow follows the true path, and another block when control flow follows the false path). So, that is essentially how the 6 blocks are determined. But, that's not the end of the story.&lt;/p&gt;

&lt;p&gt;If we pass a Boolean true to both cond_1 and cond_2 conditional clauses the block coverage measure in BlockExample1 results in 85.71% coverage; however, the block coverage measure for BlockExample2 actually results in 100% coverage as illustrated below.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/coverage_2.jpg"&gt;&lt;img title="coverage" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="235" alt="coverage" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/coverage_thumb.jpg" width="715" border="0" /&gt;&lt;/a&gt; What? How can this be? Both BlockExample1 and BlockExample2 are syntactically identical. Well, to understand this we would really need to dig deeper into compilers and coverage tools. That is well beyond the boundaries of this blog, but the IL does provide some insight.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/msil_2.jpg"&gt;&lt;img title="msil" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="677" alt="msil" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/msil_thumb.jpg" width="727" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The MSIL for BlockExample1 is on the left and BlockExample2 is on the right. Now, I don't want to do a deep dive into MSIL, but&amp;#160; those who are really observant can see that for some reason the Visual Studio compiler evaluated a branch in BlockExample1 to false (instruction IL_0008), and then instruction IL_000c compares the 2 values for equality and instruction IL_0015 appears to evaluate the optimized compound conditional expression to true. Compare that to BlockExample2 MSIL which shows the first comparison of 2 values occurs at IL_0009 and the branch is evaluated as true (IL_000f) and the second comparison of 2 values occurs at IL_0014 and again evaluates to true at instruction IL_001a. &lt;/p&gt;

&lt;p&gt;But wait…it gets even more confusing. We typically measure structural coverage using the debug build. So, imagine my surprise when I recompiled the code using the retail build settings and again passed true arguments to the cond_1 and cond_2 parameters for BlockExample1 and BlockExample2 and the coverage tool in Visual Studio indicated these methods now only had 4 blocks, and the block coverage measure for both methods was 100% as illustrated below. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/coverage2_2.jpg"&gt;&lt;img title="coverage2" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="207" alt="coverage2" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/coverage2_thumb.jpg" width="694" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, interestingly enough the compiler optimized the code so both methods had identical MSIL op code instructions as illustrated below.&lt;a href="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/be2_2.jpg"&gt;&lt;img title="be2" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="513" alt="be2" src="http://blogs.msdn.com/blogfiles/imtesty/WindowsLiveWriter/ed1c2ebc6677_79E3/be2_thumb.jpg" width="674" border="0" /&gt;&lt;/a&gt;Steve Carroll (a senior developer in Visual Studio) &lt;a href="http://blogs.msdn.com/scarroll/archive/2005/01/14/353427.aspx" target="_blank"&gt;wrote&lt;/a&gt; we &amp;quot;&lt;em&gt;shouldn't be too concerned if you can't exactly identify where all the blocks are.&amp;#160; When you turn the optimizer on your binary, block counts are fairly unpredictable&lt;/em&gt;. &lt;em&gt;Don't worry though, the source line coloring will almost always lead you to the parts of the code that you need to worry about targeting to get your coverage stats up.&lt;/em&gt;&amp;quot;&lt;/p&gt;

&lt;p&gt;I agree with Steve when he states block counts are unpredictable when the code is optimized (and different tools that measure block coverage may provide different results). However, I only partially with his statement that source line coloring leading us to parts of the code we need to test. Maybe it will, maybe it won't. But, professional testers performing an in-depth analysis of code coverage results will help us identify important parts of the code that require further investigation and testing.&lt;/p&gt;

&lt;h1&gt;&lt;strong&gt;So, what does it all mean?&lt;/strong&gt;&lt;/h1&gt;

&lt;p&gt;Block testing is useful for unit testing and designing white box tests for switch statements and exception handlers (based on how we can track control flow through source code using a debugger as opposed to through the IL Disassembler). But, as I stated in &lt;em&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0735624259/compendiumdev-20" target="_blank"&gt;How We Test Software at Microsoft&lt;/a&gt;&lt;/em&gt; block testing is the weakest form of structural testing. But, it does provide a different perspective as compared to other structural approaches or techniques and is useful when used by a professional tester in the right context.&lt;/p&gt;

&lt;p&gt;But, the important point here is that just as we wouldn't rely on only one tool to tune the carburetor on an automobile, we certainly would rely on only one technique or approach for designing structural tests; and we certainly wouldn't only rely on structural testing as a single approach to testing. This example further reinforces another important point that I make in the book; code coverage is not directly related to quality. Any professional tester can clearly see that although we are able to achieve high levels of coverage with one test, these methods are not at all well tested. &lt;/p&gt;

&lt;p&gt;Only a fool would use code coverage metrics to derive some measure of quality, or suggest the implication that high coverage measures equal greater quality. In truth, the value of code coverage is in its ability to help professional testers identify areas of the code that have not been previously exercised and to design tests to evaluate those areas of the code more effectively to help reduce overall risk. &lt;/p&gt;

&lt;p&gt;If we don't execute an area of code then we have zero probability of exposing errors in that code if they exist. However, just because we do execute a code statement doesn't mean we expose all potential errors. But, it at least increases the probability from 0% and helps reduce risk.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9461895" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/imtesty/archive/tags/The+Professional+Tester/default.aspx">The Professional Tester</category><category domain="http://blogs.msdn.com/imtesty/archive/tags/Testing/default.aspx">Testing</category></item></channel></rss>