<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3321529878072237426</id><updated>2011-12-26T10:15:59.178-08:00</updated><category term='Visual Studio'/><category term='JsTestDriver'/><category term='.Net ResolveUrl'/><category term='OLAP'/><category term='debugging'/><category term='refactoring'/><category term='Selenium'/><category term='macros'/><category term='UtcSetLastModified HttpCachePolicy timezone IIS'/><category term='MDX'/><category term='ASP.NET'/><category term='Web Tests'/><category term='Unit Tests'/><title type='text'>programming narratives</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-6938178253354898908</id><published>2010-03-20T05:38:00.001-07:00</published><updated>2011-09-10T06:57:43.067-07:00</updated><title type='text'>Moles from Microsoft. Just another stubbing framework? Not really…</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Recently Microsoft research lab announced Moles stubbing framework that comes tightly with Pex (&lt;a href="http://research.microsoft.com/en-us/projects/pex/"&gt;http://research.microsoft.com/en-us/projects/pex/&lt;/a&gt;). I really do not see profits in using Pex on real projects, but Moles is quite interesting.&lt;br /&gt;&lt;br /&gt;Features of Moles:&lt;br /&gt;1) It can create &lt;a href="http://xunitpatterns.com/Test%20Double.html"&gt;all types of test doubles&lt;/a&gt; except of mocks. Personally I do not accept &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html#SoShouldIBeAClassicistOrAMockist"&gt;mockist style&lt;/a&gt; of unit testing, so I consider absence of mocks as advantage.&lt;br /&gt;&lt;br /&gt;2) It uses delegates to define behavior of test doubles. Like &lt;a href="http://code.google.com/p/moq/"&gt;Moq&lt;/a&gt; does. Sweet :)&lt;br /&gt;&lt;pre style="background: #ffffff; color: black;"&gt;var fileSystem &lt;span style="color: #808030;"&gt;=&lt;/span&gt; &lt;span style="color: maroon; font-weight: bold;"&gt;new&lt;/span&gt; SIFileSystem&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: purple;"&gt;{&lt;/span&gt;&lt;br /&gt;    ReadAllTextString &lt;span style="color: #808030;"&gt;=&lt;/span&gt; filename &lt;span style="color: #808030;"&gt;=&lt;/span&gt;&lt;span style="color: #808030;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;        &lt;span style="color: purple;"&gt;{&lt;/span&gt; &lt;br /&gt;            Assert&lt;span style="color: #808030;"&gt;.&lt;/span&gt;AreEqual&lt;span style="color: #808030;"&gt;(&lt;/span&gt;fileName&lt;span style="color: #808030;"&gt;,&lt;/span&gt; file&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: purple;"&gt;;&lt;/span&gt; &lt;br /&gt;            &lt;span style="color: maroon; font-weight: bold;"&gt;return&lt;/span&gt; content&lt;span style="color: purple;"&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: purple;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: purple;"&gt;}&lt;/span&gt;&lt;span style="color: purple;"&gt;;&lt;/span&gt;&lt;/pre&gt;3) It uses pre-compile time code generation to create classes for doubles.&lt;br /&gt;&lt;br /&gt;4) It can be used with any testing framework, like NUnit, MBUnit, xUnit. Of course it does – in other case nobody will use it!&lt;br /&gt;&lt;br /&gt;5) And the last and most significant feature – &lt;strong&gt;it can replace any class with test double, even static one!&lt;/strong&gt; Before this moment only one framework was to able do it – &lt;a href="http://site.typemock.com/"&gt;typemock&lt;/a&gt; for just 800 $ per license. They call such test doubles “Moles” and mole for DateTime.Now will look like:&lt;br /&gt;&lt;pre style="background: #ffffff; color: black;"&gt;MDateTime&lt;span style="color: #808030;"&gt;.&lt;/span&gt;NowGet &lt;span style="color: #808030;"&gt;=&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;=&lt;/span&gt;&lt;span style="color: #808030;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: maroon; font-weight: bold;"&gt;new&lt;/span&gt; DateTime&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: #008c00;"&gt;2010&lt;/span&gt;&lt;span style="color: #808030;"&gt;,&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt;&lt;span style="color: #808030;"&gt;,&lt;/span&gt; &lt;span style="color: #008c00;"&gt;20&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: purple;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Finally covering legacy code with unit tests will be not so painful :)&lt;br /&gt;&lt;br /&gt;Links for farther reading: &lt;br /&gt;&lt;a href="http://angler.wordpress.com/2010/01/21/pex-and-moles-untestable-code-not-really"&gt;http://angler.wordpress.com/2010/01/21/pex-and-moles-untestable-code-not-really&lt;/a&gt;&lt;br /&gt;&lt;a href="http://research.microsoft.com/en-us/projects/pex/documentation.aspx"&gt;http://research.microsoft.com/en-us/projects/pex/documentation.aspx&lt;/a&gt;&lt;br /&gt;&lt;a href="http://research.microsoft.com/en-us/projects/pex/molestutorial.docx"&gt;http://research.microsoft.com/en-us/projects/pex/molestutorial.docx&lt;/a&gt;&lt;br /&gt;&lt;a href="http://research.microsoft.com/en-us/projects/pex/molesmanual.docx"&gt;http://research.microsoft.com/en-us/projects/pex/molesmanual.docx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Answer to question "Is Pex (Test generation) really usefull tool?" is here &lt;a href="http://stackoverflow.com/questions/2704669/is-pex-test-generation-really-usefull-tool"&gt;http://stackoverflow.com/questions/2704669/is-pex-test-generation-really-usefull-tool&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-6938178253354898908?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/6938178253354898908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=6938178253354898908' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6938178253354898908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6938178253354898908'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2010/03/moles-from-microsoft-just-another.html' title='Moles from Microsoft. Just another stubbing framework? Not really…'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-4811574780563085984</id><published>2010-01-21T14:18:00.001-08:00</published><updated>2010-01-21T14:18:30.326-08:00</updated><title type='text'>Solutions for problems of automated testing Web applications through UI</title><content type='html'>&lt;p&gt;Here is the list of general problems of testing Web applications through UI:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Maintainability&lt;/li&gt;    &lt;li&gt;Hard to create for non-programmers&lt;/li&gt;    &lt;li&gt;Hard to handle AJAX&lt;/li&gt;    &lt;li&gt;Hard to handle persistence&lt;/li&gt;    &lt;li&gt;Erratic tests&lt;/li&gt;    &lt;li&gt;Testing takes too much time&lt;/li&gt;    &lt;li&gt;Hard to understand test flow&lt;/li&gt;    &lt;li&gt;Hard to understand why test failed&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Project &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; was created to show how to create automated UI tests for ASP.NET application using Selenium and minify influence of these problems. Description of how each particular issue can be avoided:&lt;/p&gt;  &lt;h5&gt;Problem: Maintainability&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Abstraction layers (&lt;a href="http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html"&gt;Page Object&lt;/a&gt;, &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_09.html"&gt;Flow&lt;/a&gt;)&lt;/li&gt;    &lt;li&gt;Code generation - &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet.html"&gt;Part 1&lt;/a&gt; and &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_1303.html"&gt;Part 2&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_24.html"&gt;Continuous integration&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Processes (Do not allow commit when build fails)&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Hard to create for non-programmers&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet-dsl.html"&gt;Domain Specific Language&lt;/a&gt; and lightweight editor/runner&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Hard to handle AJAX&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Just tricky technical problem - &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_13.html"&gt;Part 1&lt;/a&gt; and &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_17.html"&gt;Part 2&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Hard to handle persistence&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_90.html"&gt;Fresh test fixture&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;Use unique data for each test&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Erratic tests&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_24.html"&gt;Continuous integration&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Processes (&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_30.html"&gt;track such tests, make problem visible and avoid it&lt;/a&gt;)&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Testing takes too much time&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_19.html"&gt;Run tests in parallel and distribute tests on several machines&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Hard to understand test flow&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Abstraction layers (&lt;a href="http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html"&gt;Page Object&lt;/a&gt;, &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_09.html"&gt;Flow&lt;/a&gt;)&lt;/li&gt;    &lt;li&gt;Think about future generations :)&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;Problem: Hard to understand why test failed&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Special patterns (&lt;a href="http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html"&gt;Navigator&lt;/a&gt;)&lt;/li&gt;    &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_16.html"&gt;Meaningful messages&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_30.html"&gt;Run time analysis&lt;/a&gt;&amp;#160;&lt;/li&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-4811574780563085984?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/4811574780563085984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=4811574780563085984' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/4811574780563085984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/4811574780563085984'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2010/01/solutions-for-problems-of-automated.html' title='Solutions for problems of automated testing Web applications through UI'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-3332482623287832347</id><published>2010-01-04T05:15:00.001-08:00</published><updated>2010-01-04T05:16:48.817-08:00</updated><title type='text'>Nant task to calculate GMT or UTC timestamp</title><content type='html'>&lt;p&gt;I was surprised when i realized that standard implementation of &lt;a href="http://nant.sourceforge.net/release/latest/help/tasks/tstamp.html"&gt;&amp;lt;tstamp&amp;gt;&lt;/a&gt; task do not support getting time in GMT time zone. I have not found any custom extensions for nant, which will provide such functionality so i was forced to write my own custom task. Fortunately it is really easy:&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;    &amp;lt;script language="C#" prefix="test" &amp;gt;&lt;br /&gt; &amp;lt;code&amp;gt;&lt;br /&gt;   &amp;lt;![CDATA[&lt;br /&gt;  [TaskName("gmttimestamp")]&lt;br /&gt;  public class gmttimestamp : Task&lt;br /&gt;  {&lt;br /&gt;   private string _pattern;&lt;br /&gt;   private string _property;&lt;br /&gt;&lt;br /&gt;   [TaskAttribute("pattern", Required = true)]&lt;br /&gt;   public string Pattern&lt;br /&gt;   {&lt;br /&gt;    get { return _pattern; }&lt;br /&gt;    set { _pattern = value; }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   [TaskAttribute("property", Required = true)]&lt;br /&gt;   public string Property&lt;br /&gt;   {&lt;br /&gt;    get { return _property; }&lt;br /&gt;    set { _property = value; }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   protected override void ExecuteTask()&lt;br /&gt;   {&lt;br /&gt;    string gmtTime = DateTime.UtcNow.ToString(_pattern);&lt;br /&gt;    Properties.Add(_property, gmtTime);&lt;br /&gt;&lt;br /&gt;    Log(Level.Info, "[gmttimestamp] :" + gmtTime);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;   ]]&amp;gt;&lt;br /&gt; &amp;lt;/code&amp;gt;&lt;br /&gt;   &amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;p&gt;Usage in Nant build script:&lt;/p&gt;&lt;pre class="c#" name="code"&gt; &amp;lt;gmttimestamp property='today' pattern="yyMMdd"/&amp;gt;&lt;br /&gt; &amp;lt;gmttimestamp property='start.selenium.timestamp' pattern="HH:mm:ss"/&amp;gt;&lt;/pre&gt;&lt;p&gt;Fully integrated task can be found here: &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build"&gt;http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-3332482623287832347?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/3332482623287832347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=3332482623287832347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3332482623287832347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3332482623287832347'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2010/01/nant-task-to-calculate-gmt-or-utc.html' title='Nant task to calculate GMT or UTC timestamp'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-7811312315141373655</id><published>2009-12-30T04:22:00.001-08:00</published><updated>2010-01-04T09:13:02.015-08:00</updated><title type='text'>Design of Selenium tests for ASP.NET: Finding the source of erratic bugs</title><content type='html'>&lt;p style="text-align: left;"&gt;After week of running UI tests as a part of continuous integration build &lt;strong&gt;without modifications in &lt;/strong&gt;&lt;a href="http://xunitpatterns.com/SUT.html"&gt;&lt;strong&gt;SUT&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; code&lt;/strong&gt; I realized that significant part of tests runs is failed! I know that UI tests are erratic by their nature, because they have a lot of dependencies, which you can’t control, but 40% of failures is too much. Also, when i run this tests on local without Selenium Grid cluster, all tests always pass.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SztFyxZwvwI/AAAAAAAAGA0/M8VlkxTvi2o/s1600-h/image%5B6%5D.png"&gt;&lt;img title="Selenium tests build overview" 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="351" alt="Selenium tests build overview" src="http://lh4.ggpht.com/_aKmEmrJWYOY/SztF02d9pYI/AAAAAAAAGA4/D0PiuzkGmo4/image_thumb%5B4%5D.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;This needs to be fixed. Lets check error messages:&lt;/p&gt;  &lt;p&gt;1) Test &lt;strong&gt;CheckNameOnLoginPage&lt;/strong&gt;: &lt;em&gt;ERROR: Element xpath=/html/body/form[@id='form1']/div[3]/table/tbody/tr[2]/td[2]/input[@id='txtUser'] not found&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;2) Test &lt;strong&gt;CheckCurrentUserName&lt;/strong&gt;: &lt;span class="logStreamMarker-Exception"&gt;&lt;em&gt;&lt;span class="logStreamMarker-ExceptionType"&gt;System&lt;wbr&gt;.Exception&lt;/span&gt;&lt;wbr&gt;: &lt;wbr&gt;&lt;span class="logStreamMarker-ExceptionMessage"&gt;Error &lt;wbr&gt;while &lt;wbr&gt;executing &lt;wbr&gt;command &lt;wbr&gt;AssertUserName&lt;/span&gt; &lt;wbr&gt;---&amp;gt;&lt;wbr&gt; &lt;wbr&gt;&lt;span class="logStreamMarker-Exception"&gt;&lt;span class="logStreamMarker-ExceptionType"&gt;System&lt;wbr&gt;.Reflection&lt;wbr&gt;.TargetInvocationExc&lt;wbr&gt;eption&lt;/span&gt;&lt;wbr&gt;: &lt;wbr&gt;&lt;span class="logStreamMarker-ExceptionMessage"&gt;Exception &lt;wbr&gt;has &lt;wbr&gt;been &lt;wbr&gt;thrown &lt;wbr&gt;by &lt;wbr&gt;the &lt;wbr&gt;target &lt;wbr&gt;of &lt;wbr&gt;an &lt;wbr&gt;invocation&lt;wbr&gt;.&lt;/span&gt; &lt;wbr&gt;---&amp;gt;&lt;wbr&gt; &lt;wbr&gt;&lt;span class="logStreamMarker-Exception"&gt;&lt;span class="logStreamMarker-ExceptionType"&gt;Selenium&lt;wbr&gt;.SeleniumException&lt;/span&gt;&lt;wbr&gt;: &lt;wbr&gt;&lt;span class="logStreamMarker-ExceptionMessage"&gt;Timed &lt;wbr&gt;out &lt;wbr&gt;after &lt;wbr&gt;20000ms&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span class="logStreamMarker-Exception"&gt;&lt;span class="logStreamMarker-Exception"&gt;&lt;span class="logStreamMarker-Exception"&gt;&lt;span class="logStreamMarker-ExceptionMessage"&gt;3) Test &lt;strong&gt;NewlyAddedUserAppearsInUsersList:&lt;/strong&gt; &lt;span class="logStreamMarker-Exception"&gt;&lt;em&gt;&lt;span class="logStreamMarker-ExceptionType"&gt;Selenium&lt;wbr&gt;.SeleniumException&lt;/span&gt;&lt;wbr&gt;: &lt;wbr&gt;&lt;span class="logStreamMarker-ExceptionMessage"&gt;Timed &lt;wbr&gt;out &lt;wbr&gt;after &lt;wbr&gt;20000ms&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Notice that always appears in random tests and always connected to absence elements. I observed test runs and found out that sometimes IIS error page with message “403.9 Access Forbidden Too many users are connected” appears in browser.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SztF3GpDVJI/AAAAAAAAGA8/rC-OOJXTtiA/s1600-h/image%5B12%5D.png"&gt;&lt;img title="403.9 Access Forbidden Too many users are connected" 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="461" alt="403.9 Access Forbidden Too many users are connected" src="http://lh6.ggpht.com/_aKmEmrJWYOY/SztF5hgckII/AAAAAAAAGBA/lrIqHv6fGpc/image_thumb%5B8%5D.png?imgmax=800" width="433" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here is it. IIS in windows of “professional” version has a limit of 10 users and therefore sometimes whole page is not loaded or just several JavaScript files and this breaks whole client side application. Here is the IIS log:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;08:51:30 10.6.9.180 POST /SampleApplication/Login.aspx 403   &lt;br /&gt;08:51:30 10.6.9.180 GET /SampleApplication/Login.aspx 403&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;….&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;15:10:57 10.6.24.72 POST /SampleApplication/Login.aspx 302   &lt;br /&gt;15:10:57 10.6.24.72 POST /SampleApplication/Login.aspx 302    &lt;br /&gt;15:10:57 10.6.24.72 GET /SampleApplication/Home.aspx 200    &lt;br /&gt;15:10:57 10.6.24.72 GET /SampleApplication/Home.aspx 200    &lt;br /&gt;15:10:57 10.6.24.72 GET /SampleApplication/js/Frameworks/jquery-1.3.1.js 200    &lt;br /&gt;…    &lt;br /&gt;15:10:57 10.6.24.72 GET /SampleApplication/js/Services/Services.js 403&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;…&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;It is only needed to upgrade to IIS 7 to fix it. But i already spent a lot of time (about 5 hours) to find out what happened and do not want to deal with this problem in future.  &lt;/p&gt;  &lt;p&gt;First let’s learn tests to recognize IIS error pages:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;private static void AssertErrorPage&amp;lt;TT&amp;gt;(TT target) where TT : PageBase, new()&lt;br /&gt;     {&lt;br /&gt;         var bodyText = target.Selenium.GetBodyText();&lt;br /&gt;&lt;br /&gt;         if (bodyText.Contains("Server Error in "))&lt;br /&gt;         {&lt;br /&gt;             Assert.Fail("Server error while navigating\r\n\r\n {0}.", bodyText);&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;         if (bodyText.Contains("Internet Information Services") &amp;amp;&amp;amp; bodyText.Contains("Microsoft Support"))&lt;br /&gt;         {&lt;br /&gt;             Assert.Fail("IIS error while navigating\r\n\r\n {0}.", bodyText);&lt;br /&gt;         }&lt;br /&gt;     }&lt;/pre&gt;&lt;p&gt;And include iis log analysis in continuous  report. &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&amp;amp;displaylang=en"&gt;LogParser&lt;/a&gt; application can be used for this purposes – it supports SQL – like queries to logs. Here is sql to select log items from time to time into xml:&lt;/p&gt;&lt;pre class="sql" name="code"&gt;SELECT *&lt;br /&gt;FROM %logdir%\ex%today%.log&lt;br /&gt;TO %reportsdir%\extracted_iis_log.xml&lt;br /&gt;WHERE TO_TIME(time) BETWEEN TIMESTAMP('%starttime%', 'hh:mm:ss') AND TIMESTAMP('%endtime%', 'hh:mm:ss') &lt;/pre&gt;&lt;p&gt;All details how to run logparser in nant script can be found here: &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build"&gt;&lt;u&gt;&lt;span style="color:#0000ff;"&gt;http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build&lt;/span&gt;&lt;/u&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Xsl to integrate iis logs in CCNET report is here: &lt;u&gt;&lt;span style="color:#0000ff;"&gt;&lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/ccnet/iisloganalyser.xsl"&gt;http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/ccnet/iisloganalyser.xsl&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;After this modifications we can review reports for bugs mentioned above:&lt;/p&gt;&lt;p&gt; &lt;em&gt;An assertion failed.&lt;br /&gt;&lt;br /&gt;IIS error while navigating&lt;/em&gt;&lt;/p&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;The page cannot be displayed There are too many people accessing the Web site at this time. &lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;em&gt;Please try the following:&lt;br /&gt;&lt;br /&gt;  Click the Refresh button, or try again later.&lt;br /&gt;&lt;br /&gt;  Open the epbyminw0115.minsk.epam.com home page, and then look for links to the information you want. HTTP 403.9 - Access Forbidden: Too many users are connected&lt;br /&gt;&lt;br /&gt;  Internet Information Services&lt;br /&gt;&lt;br /&gt;  Technical Information (for support personnel)&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Background:&lt;br /&gt;&lt;br /&gt;  This error can occur if the Web server is busy and cannot process your request due to heavy traffic.&lt;br /&gt;&lt;br /&gt; More information:&lt;br /&gt;Microsoft Support.&lt;/em&gt;&lt;/p&gt;&lt;br /&gt;And IIS logs are saved for us right in the CCNET report:&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/S0IgshSmDLI/AAAAAAAAGBE/w2Zs15OV0eo/s1600-h/image%5B5%5D.png" style="text-decoration: none;"&gt;&lt;img title="iis log in ccnet with 403 error" style="text-align: center;border-right-width: 0px; border-right-style: initial; border-right-color: initial; border-top-width: 0px; border-top-style: initial; border-top-color: initial; display: block; float: none; margin-left: auto; border-left-width: 0px; border-left-style: initial; border-left-color: initial; margin-right: auto; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; " height="307" alt="iis log in ccnet with 403 error" src="http://lh4.ggpht.com/_aKmEmrJWYOY/S0Igtja_AZI/AAAAAAAAGBI/9HvalFgESXk/image_thumb%5B2%5D.png?imgmax=800" width="670" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="text-align: center;"&gt;&lt;a href="http://lh4.ggpht.com/_aKmEmrJWYOY/S0Igu89udGI/AAAAAAAAGBM/cnfWsSGOEJ0/s1600-h/image%5B9%5D.png"&gt;&lt;img title="iis log in ccnet with 403 error" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="299" alt="iis log in ccnet with 403 error" src="http://lh6.ggpht.com/_aKmEmrJWYOY/S0IgwOdrnYI/AAAAAAAAGBQ/_6KM20E8Euo/image_thumb%5B4%5D.png?imgmax=800" width="712" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;Now it is possible to identify where the problem is without deep investigation – all tips and keys to solution are available in report, and if problem will appear in future – it most likely will be resolved with low efforts.&lt;br /&gt;&lt;br /&gt;So, the moral is: if you experienced the problem in SUT or tests, first learn your tests to find and alarm about this problem, and only after that fix it. This will help you and your colleagues in future.&lt;br /&gt;&lt;br /&gt;Actually here we started to implement analysis of how system behaves in run time, and this is only beginning. In future posts i want to describe now to get statistics from memory dumps and performance probes. This will give developers priceless statistics which will help to understand problems with concurrency, performance troubles, memory leaks, “hard to reproduce” bugs.&lt;br /&gt;&lt;br /&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; solution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-7811312315141373655?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/7811312315141373655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=7811312315141373655' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/7811312315141373655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/7811312315141373655'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_30.html' title='Design of Selenium tests for ASP.NET: Finding the source of erratic bugs'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/SztF02d9pYI/AAAAAAAAGA4/D0PiuzkGmo4/s72-c/image_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-3045991508641471042</id><published>2009-12-29T05:53:00.001-08:00</published><updated>2009-12-29T05:53:32.080-08:00</updated><title type='text'>How to run selenium tests without interfering with desktop in Windows</title><content type='html'>&lt;p&gt;It is very uncomfortably to run UI tests in parallel with other work. It shows browser window over you working window, steals focus moves the mouse,&amp;#160; and you can affect test results by closing one of the testing windows. &lt;/p&gt;  &lt;p&gt;The way out – is to use “&lt;a href="http://technet.microsoft.com/en-us/sysinternals/cc817881.aspx"&gt;Desktops&lt;/a&gt;” tool from SysInternals. Now you can run tests in one desktop and work in another:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/SzoJ0wwE9PI/AAAAAAAAGAs/rxdex7_s_54/s1600-h/image%5B5%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="378" alt="image" src="http://lh4.ggpht.com/_aKmEmrJWYOY/SzoJ2gIT7-I/AAAAAAAAGAw/6l9hoJaJvO0/image_thumb%5B3%5D.png?imgmax=800" width="444" border="0" /&gt;&lt;/a&gt;This is also good option for creation of Selenium Grid cluster with developer computers as remote control runners. Tests do not load memory and processor, but they simply obstruct work by interfering with desktop. “&lt;a href="http://technet.microsoft.com/en-us/sysinternals/cc817881.aspx"&gt;Desktops&lt;/a&gt;” tool can resolve this problem and allow you to create big and cheap Selenium Grid cluster.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-3045991508641471042?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/3045991508641471042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=3045991508641471042' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3045991508641471042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3045991508641471042'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/how-to-run-selenium-tests-without.html' title='How to run selenium tests without interfering with desktop in Windows'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/SzoJ2gIT7-I/AAAAAAAAGAw/6l9hoJaJvO0/s72-c/image_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-9026293557712531713</id><published>2009-12-24T09:14:00.001-08:00</published><updated>2009-12-24T10:55:48.640-08:00</updated><title type='text'>Design of Selenium tests for ASP.NET: Running UI tests as a part of continuous integration.</title><content type='html'>&lt;p&gt;To keep UI tests up up to date it is needed to maintain them continuously. Build should be verified against UI tests every day and if something is broken – team should know what happened immediately. Only with such strategy give a chance for UI tests to survive. Just think, you are about to release build, it is late afternoon, it is needed to verify this build by QA and you realize that some of tests are broken (worth – if it broken by with strange errors). Will you fix this tests? NO. And another situation – you have just committed your changes and some automated system sends you e-mail that one use case is broken exactly after your commit. Will you fix it? Yep - it is very easy to find source of the problem.&lt;/p&gt;  &lt;p&gt;Given all this, it is have no sense to have UI tests without automated continuous integration. And lucky we are it is possible to set up this procedure with only open source tools.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Scheduler and viewer of builds - &lt;a href="http://sourceforge.net/projects/ccnet/"&gt;Cruise Control .NET&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Console client for &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://nant.sourceforge.net/"&gt;Nant&lt;/a&gt; as build system &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.gallio.org/"&gt;Gallio&lt;/a&gt; as test runner &lt;/li&gt;    &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx"&gt;PsExec&lt;/a&gt; tool to open browsers on remote computers.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Here are the solutions to main issues of running Selenium Grid on CCNET: &lt;/p&gt;  &lt;p&gt;1) CCNET is that its worker process is implemented as windows service. Browsers just can't start without desktop. This can be resolved by PsExec tool, which can run processes in desktop session of particular user.&lt;/p&gt;  &lt;p&gt;2) CCNET service should be runned under user, which have access to all servers, where remote controls will be executed.&lt;/p&gt;  &lt;p&gt;3) PsExec shows window with terms of use first time, so exec it manually before using it with service.&lt;/p&gt;  &lt;p&gt;The example of CCNet config file can be found &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/ccnet.config"&gt;here.&lt;/a&gt; And all builds scripts which download latest version of sources, adjust configuration files, build application and tests binaries, starts selenium grid on server and slave computers and run tests are &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/#svn/trunk/_build/scripts"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Build in Sample application starts every time after code commit. But if your tests take more then 10 minutes to run – divide your tests into 2 tests suits – one for checking after commit, and one for checking every night – this still will keep your tests up to date. &lt;/p&gt;  &lt;p&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; solution.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-9026293557712531713?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/9026293557712531713/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=9026293557712531713' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/9026293557712531713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/9026293557712531713'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_24.html' title='Design of Selenium tests for ASP.NET: Running UI tests as a part of continuous integration.'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-6401768134597951090</id><published>2009-12-23T01:44:00.001-08:00</published><updated>2010-01-12T08:56:04.278-08:00</updated><title type='text'>How to run Selenium Grid hub and remote control through nant or command line</title><content type='html'>&lt;p&gt;On the official site of &lt;a href="http://selenium-grid.seleniumhq.org/"&gt;Selenium Grid&lt;/a&gt; there is no description how to run it without ant. But download this tool just to start grid is not very convenient, so i wrote tasks for nant and found the way to run Grid from command line:&lt;/p&gt;  &lt;p&gt;First define properties: &lt;/p&gt;  &lt;pre class="xml" name="code"&gt;      &amp;lt;property name="selenium.server.file" value="${src.dir}\_tools\selenium\selenium-server.jar" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="selenium.grid.hub.file" value="${src.dir}\_tools\selenium\selenium-grid-hub-standalone-1.0.4.jar" /&amp;gt;&lt;br /&gt;    &amp;lt;property name="selenium.grid.rc.file" value="${src.dir}\_tools\selenium\selenium-grid-remote-control-standalone-1.0.4.jar" /&amp;gt;&lt;/pre&gt;task to start hub:&lt;br /&gt;&lt;pre class="xml" name="code"&gt;     &amp;lt;target name="start.selenium.grid.hub"&amp;gt;&lt;br /&gt;      &amp;lt;exec program="java" verbose="true" failonerror="false"&amp;gt;&lt;br /&gt;        &amp;lt;arg value="-jar" /&amp;gt;&lt;br /&gt;        &amp;lt;arg value="${selenium.grid.hub.file}" /&amp;gt;&lt;br /&gt;      &amp;lt;/exec&amp;gt;&lt;br /&gt;    &amp;lt;/target&amp;gt;&lt;/pre&gt;task to start remote control:&lt;br /&gt;&lt;pre class="xml" name="code"&gt;    &amp;lt;target name="start.selenium.grid.rc"&amp;gt;&lt;br /&gt;      &amp;lt;exec program="java" verbose="true" failonerror="false"&amp;gt;&lt;br /&gt;        &amp;lt;arg value="-classpath" /&amp;gt;&lt;br /&gt;        &amp;lt;arg value="${selenium.server.file};${selenium.grid.rc.file}" /&amp;gt;&lt;br /&gt;        &amp;lt;arg value="com.thoughtworks.selenium.grid.remotecontrol.SelfRegisteringRemoteControlLauncher" /&amp;gt;&lt;br /&gt;      &amp;lt;/exec&amp;gt;&lt;br /&gt;    &amp;lt;/target&amp;gt;&lt;/pre&gt;&lt;p&gt;Or simply from command line:&lt;/p&gt;&lt;p&gt;Hub:&lt;/p&gt;&lt;p&gt;&lt;em&gt;java -jar D:\work\SeleniumDesign\build_artifacts\artifacts\continuous\source\_tools\selenium\selenium-grid-hub-standalone-1.0.4.jar&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Remote Control:&lt;/p&gt;&lt;p&gt;&lt;em&gt;java -classpath D:\work\SeleniumDesign\build_artifacts\artifacts\continuous\source\_tools\selenium\selenium-server.jar;D:\work\SeleniumDesign\build_artifacts\artifacts\continuous\source\_tools\selenium\selenium-grid-remote-control-standalone-1.0.4.jar com.thoughtworks.selenium.grid.remotecontrol.SelfRegisteringRemoteControlLauncher&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Examples of build script which run selenium grid can be found here: &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build"&gt;http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/_build/scripts/continuous/main.build&lt;/a&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-6401768134597951090?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/6401768134597951090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=6401768134597951090' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6401768134597951090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6401768134597951090'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/how-to-run-selenium-grid-hub-and-remote.html' title='How to run Selenium Grid hub and remote control through nant or command line'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-8527301368886126596</id><published>2009-12-19T04:09:00.001-08:00</published><updated>2009-12-19T06:54:46.650-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Running Web Tests in parallel using Selenium GRID</title><content type='html'>&lt;p&gt;One of the biggest problems of Web tests is testing duration. On one of my projects we had fully automated acceptance test suite written on &lt;a href="http://watin.sourceforge.net/"&gt;Watin&lt;/a&gt;, and it takes more then 6 hours to run it… It was damn frustrating to see night build failed – because it is needed to fix it and run it again and wait for 6 hours again.&lt;/p&gt;  &lt;p&gt;The faster smoke test, then you will know about any problems with your application earlier, ideally after each change. In this case it is possible to locate problem in minimal time (it is easier to find problem in one small changeset, but not very easy in daily changeset, which was done by whole team).&lt;/p&gt;  &lt;p&gt;Selenium already has a system, which can dramatically speed up web tests – &lt;a href="http://selenium-grid.seleniumhq.org/"&gt;Selenium Grid&lt;/a&gt;:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;An efficient development cycle requires prompt and reliable feedback. Stop waiting hours to get the results of your web acceptance builds! Selenium Grid transparently distribute your tests on multiple machines so that you can run your tests in parallel, cutting down the time required for running in-browser test suites. This will dramatically speeds up in-browser web testing, giving you quick and accurate feedback you can rely on to improve your web application.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This is amazing tool – i just dreamed to have such system for Watin, luckily we have it for Selenium!&lt;/p&gt;  &lt;p&gt;The idea of Grid is simple – it mocks usual  Selenium Server interface, but runs a lot of Selenium Server instances behind. This means that tests, which was designed for usual Selenium will be executed on Grid without changes.  &lt;/p&gt;  &lt;p&gt;To install Grid and run it follow instructions from &lt;a href="http://selenium-grid.seleniumhq.org/get_started.html"&gt;here&lt;/a&gt;. Run hub and two remote controls:&lt;/p&gt;  &lt;p&gt;ant.bat launch-hub&lt;/p&gt;  &lt;p&gt;ant.bat launch-remote-control -Dport 5555&lt;/p&gt;  &lt;p&gt;ant.bat launch-remote-control -Dport 5556&lt;/p&gt;  &lt;p&gt;Console will be available by link &lt;a href="http://localhost:4444/console"&gt;&lt;u&gt;&lt;span style="color:#0000ff;"&gt;http://localhost:4444/console&lt;/span&gt;&lt;/u&gt;&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_aKmEmrJWYOY/SyzCgCC77hI/AAAAAAAAF-4/U37ieTLIjRs/s1600-h/image%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_aKmEmrJWYOY/SyzChBg1PhI/AAAAAAAAF-8/dWxyx7AfJSQ/image_thumb%5B3%5D.png?imgmax=800" width="244" height="168" /&gt;&lt;/a&gt; That is it. Now simply run your test bunch and you will get the result. Notice that tests written on c# are executed one after another.&lt;/p&gt;  &lt;p&gt;To gain benefits of parallel executing, all test runners must pass command to Grid in parallel. Unfortunately nunit runner can not run tests written on c# in parallel. This feature has complex &lt;a href="http://nunit.com/index.php?p=pnunit&amp;amp;r=2.5"&gt;PNUnit runner&lt;/a&gt; which it is very hard to setup and maintain – this is big disadvantage. From the other side &lt;a href="http://www.mbunit.com/"&gt;MbUnit&lt;/a&gt; framework has simple &lt;a href="http://www.gallio.org/api/html/T_MbUnit_Framework_ParallelizableAttribute.htm"&gt;Parallelizable attribute&lt;/a&gt; which gives us exactly what is needed – it runs tests in parallel. &lt;/p&gt;  &lt;p&gt;Place next attributes in AssemblyInfo.cs and all tests in this assembly become parallel: &lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;[assembly: DegreeOfParallelism(10)]&lt;br /&gt;[assembly: Parallelizable(TestScope.All)]&lt;/pre&gt;&lt;p&gt;The number in “DegreeOfParallelism” attribute shows how many threads will be executed.&lt;/p&gt;&lt;p&gt;The difference between non-parallel run:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_aKmEmrJWYOY/SyzCiRXO6UI/AAAAAAAAF_A/qNPx8S7Zrzs/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_aKmEmrJWYOY/SyzCjUIMUBI/AAAAAAAAF_E/3LajNr-i0RE/image_thumb%5B11%5D.png?imgmax=800" width="572" height="259" /&gt;&lt;/a&gt;And parallel run with 3 remote controls:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyzCkFpC27I/AAAAAAAAF_I/ZDbUDlh-bOw/s1600-h/image%5B21%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_aKmEmrJWYOY/SyzCky3GV3I/AAAAAAAAF_M/TH8XhbarHRw/image_thumb%5B15%5D.png?imgmax=800" width="585" height="259" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It is about 40 % (50s vs 30s) of reduced time. Having 3 servers (or just regular developer workstations) in Grid will cut running time to 10 seconds.&lt;/p&gt;&lt;p&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample solution.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-8527301368886126596?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/8527301368886126596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=8527301368886126596' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8527301368886126596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8527301368886126596'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_19.html' title='Design of Selenium tests for ASP.NET: Running Web Tests in parallel using Selenium GRID'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_aKmEmrJWYOY/SyzChBg1PhI/AAAAAAAAF-8/dWxyx7AfJSQ/s72-c/image_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-5816517433642559038</id><published>2009-12-18T11:30:00.001-08:00</published><updated>2009-12-19T04:13:55.624-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: DSL (Domain Specific Language) for Acceptance Web Tests</title><content type='html'>&lt;p&gt;A large amount of people (testers, managers and even customers) can contribute Test Cases in acceptance test suite for your project, but not all do have Visual Studio to write and compile pure unit tests like this:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;     [Test]&lt;br /&gt;    public void LoginSuccessWithCorrectPassword()&lt;br /&gt;    {&lt;br /&gt;        Start&lt;br /&gt;            .GoToLoginPage()&lt;br /&gt;            .EnterCredentials("admin", "god")&lt;br /&gt;            .Login();&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;To allow them to create tests it is needed to invent some kind of DSL in the simple to edit format – TXT for example. But also it is needed to be able run these test as regular nunit tests. &lt;a href="http://nunit.com/index.php?p=testCaseSource&amp;amp;r=2.5"&gt;TestCaseSource&lt;/a&gt; attribute is exactly what is needed – it will dynamically create test case for each TXT file in folder.&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;   [TestFixture]&lt;br /&gt;public class ScriptTest : TestBase&lt;br /&gt;{&lt;br /&gt;    [Test, TestCaseSource("TestCases")]&lt;br /&gt;    public void DivideTest(CommandSet commandSet)&lt;br /&gt;    {&lt;br /&gt;        object currentFlow = Start;&lt;br /&gt;&lt;br /&gt;        foreach (var command in commandSet.Commands)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;            var method = currentFlow.GetType().GetMethod(command.Name);&lt;br /&gt;&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                var parameters = command.Parameters.ToArray();&lt;br /&gt;&lt;br /&gt;                if(parameters.Length == 0)&lt;br /&gt;                {&lt;br /&gt;                    parameters = null;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                currentFlow = method.Invoke(currentFlow, parameters);&lt;br /&gt;            }&lt;br /&gt;            catch (Exception ex)&lt;br /&gt;            {&lt;br /&gt;&lt;br /&gt;                throw new Exception(string.Format("Error while executing command {0}", command.Name), ex);&lt;br /&gt;            }&lt;br /&gt;        &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public IEnumerable TestCases&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return (from fileName in Directory.GetFiles(Configuration.ScriptsPath)&lt;br /&gt;                    let contents = File.ReadAllText(fileName)&lt;br /&gt;                    select new object[] {GetCommand(fileName, contents)})&lt;br /&gt;                    .Cast&amp;lt;object&amp;gt;()&lt;br /&gt;                    .ToList();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static CommandSet GetCommand(string fileName, string commands)&lt;br /&gt;    {&lt;br /&gt;        var set = new CommandSet { Name = fileName };&lt;br /&gt;&lt;br /&gt;        var lines = commands.Split('\n');&lt;br /&gt;&lt;br /&gt;        lines = lines.Select(l =&amp;gt; l.Replace("\r", "")).ToArray();&lt;br /&gt;&lt;br /&gt;        foreach (var line in lines)&lt;br /&gt;        {&lt;br /&gt;            var commandStrings = line.Split(' ');&lt;br /&gt;       &lt;br /&gt;            var name = commandStrings[0];&lt;br /&gt;            var parameters = commandStrings.Skip(1);&lt;br /&gt;   &lt;br /&gt;            var command = new Command() { Name = name };&lt;br /&gt;            command.Parameters.AddRange(parameters.Cast&amp;lt;object&amp;gt;());&lt;br /&gt;&lt;br /&gt;            set.Commands.Add(command);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return set;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now just it is only needed to place TXT files in Scripts folder. Format of scripts is simple:&lt;/p&gt;&lt;p&gt;﻿&lt;strong&gt;CheckNameOnLoginPage.txt&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;LoginAndGoToHomePage&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;AssertUserName admin&lt;/em&gt;&lt;/p&gt;&lt;p&gt;or&lt;/p&gt;&lt;p&gt;&lt;strong&gt;LoginSuccessWithCorrectPassword.txt&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;GoToLoginPage&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;EnterCredentials admin god&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Login&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The result in nunit runner will be like this:&lt;/p&gt;&lt;p&gt; &lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/SyvYSziPFOI/AAAAAAAAF-s/55OWKBSIlKw/s1600-h/image%5B4%5D.png" style="text-decoration: none;"&gt;&lt;img title="NUnit result with test case marked with TestCaseSource " style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="348" alt="NUnit result with test case marked with TestCaseSource " src="http://lh4.ggpht.com/_aKmEmrJWYOY/SyvYUTmTAPI/AAAAAAAAF-w/LKPVrvCEhRM/image_thumb%5B2%5D.png?imgmax=800" width="644" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Of course all Flows are needed to be implemented before usage of them in DSL files, but the advantage of such approach is that the people who will write tests do not  have to know about aspects of interacting with web page – ids of controls, waiting for ajax requests, URLs of pages etc.&lt;/p&gt;&lt;p&gt;It would be very handful to create "help" system, which will describe all Flows with its parameters. It is easy to generate with help of Reflection.  &lt;/p&gt;&lt;p&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample solution.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-5816517433642559038?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/5816517433642559038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=5816517433642559038' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5816517433642559038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5816517433642559038'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet-dsl.html' title='Design of Selenium tests for ASP.NET: DSL (Domain Specific Language) for Acceptance Web Tests'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/SyvYUTmTAPI/AAAAAAAAF-w/LKPVrvCEhRM/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-2412851820964898118</id><published>2009-12-17T06:36:00.001-08:00</published><updated>2009-12-17T07:16:12.724-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Dealing with persistence</title><content type='html'>&lt;p&gt;Every web site have persisted storage. It can be XML on disk, DB or Key-Value storage, whatever. This aspect brings a lot of troubles while designing Web Tests. The most common example – tests script must login in system with predefined credentials and this credentials should be in storage before test run. Or another example:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt; [Test]&lt;br /&gt;      public void NewlyAddedUserCanLoginInSystem()&lt;br /&gt;      {&lt;br /&gt;          Start&lt;br /&gt;              .LoginAndGoToHomePage()&lt;br /&gt;              .AddUser("TestUserForLogin", "TestPwdForLogin")&lt;br /&gt;              .Logout()&lt;br /&gt;              .EnterCredentials("TestUserForLogin", "TestPwdForLogin")&lt;br /&gt;              .Login();&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;This test will pass only for first time – while second run user with name “TestUserForLogin” will be already in storage and inserting procedure will fail.&lt;/p&gt;To resolve this issues it is needed to:&lt;ol&gt;  &lt;li&gt;Have some initial data in storage. e.g. test user&lt;/li&gt;  &lt;li&gt;Clean up data before test suite run.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To perform this actions it is needed to have direct access to storage to the application, which allows to manage storage. Definitely it is not acceptable to insert this functions in &lt;a href="http://xunitpatterns.com/SUT.html"&gt;system under test&lt;/a&gt; – “clean database” button is the grenade waiting for the monkey:)&lt;/p&gt;&lt;p&gt;It is needed to create small standalone admin application:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_aKmEmrJWYOY/SypBnUUlH0I/AAAAAAAAFdY/MDZ8ZFjf0P4/s1600-h/image%5B3%5D.png"&gt;&lt;img title="Storage admin application" 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="219" alt="Storage admin application" src="http://lh4.ggpht.com/_aKmEmrJWYOY/SypBsz5m7fI/AAAAAAAAFdc/ECDj0oK4krQ/image_thumb%5B1%5D.png?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; Next function will be executed when button is clicked:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt; protected void ClearStorage(object sender, EventArgs e)&lt;br /&gt;      {&lt;br /&gt;          const string emptyStorage = @"&amp;lt;users&amp;gt;&amp;lt;user name=""admin"" password=""god"" /&amp;gt;&amp;lt;/users&amp;gt;";&lt;br /&gt;          var doc = new XmlDocument();&lt;br /&gt;          doc.LoadXml(emptyStorage);&lt;br /&gt;          doc.Save(new Configuration().Xml);&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;This function is designed to clean up XML, but if storage is database it will be need to execute simple script to restore database like this:&lt;/p&gt;&lt;pre class="sql" name="code"&gt;USE [master]&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'teststorage')&lt;br /&gt;ALTER DATABASE [teststorage] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'teststorage')&lt;br /&gt;DROP DATABASE [teststorage]&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;RESTORE DATABASE [teststorage] FROM  DISK = N'C:\sl\BackUp\teststorage.bak'&lt;br /&gt;WITH  FILE = 1,&lt;br /&gt;MOVE N'teststorage'&lt;br /&gt;TO N'C:\sl\Sites\teststorage.mdf',&lt;br /&gt;MOVE N'teststorage_log'&lt;br /&gt;TO N'C:\sl\Sites\teststorage.ldf',&lt;br /&gt;NOUNLOAD,  REPLACE,  STATS = 10&lt;br /&gt;GO&lt;/pre&gt;&lt;p&gt;Now it is only needed to click on this button before any test run. This is possible with &lt;a href="http://www.nunit.org/index.php?p=setupFixture&amp;amp;r=2.5"&gt;nunit “SetupFixture” attribute&lt;/a&gt;.&lt;/p&gt;&lt;blockquote&gt;  &lt;p&gt;This is the attribute that marks a class that contains the one-time setup or teardown methods for all the test fixtures under a given namespace. The class may contain at most one method marked with the SetUpAttribute and one method marked with the TearDownAttribute.&lt;/p&gt;&lt;/blockquote&gt;&lt;pre class="csharp" name="code"&gt;using NUnit.Framework;&lt;br /&gt;using Tests.SmokeTest.Core;&lt;br /&gt;using Tests.SmokeTest.PageObjects;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest&lt;br /&gt;{&lt;br /&gt;  [SetUpFixture]&lt;br /&gt;  public class SetUpSUT&lt;br /&gt;  {&lt;br /&gt;      [SetUp]&lt;br /&gt;      public void CleanUpStorage()&lt;br /&gt;      {&lt;br /&gt;          CleanUp();&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public static void CleanUp()&lt;br /&gt;      {&lt;br /&gt;          using (var navigator = new Navigator())&lt;br /&gt;          {&lt;br /&gt;              navigator.Start(Configuration.StorageAdminSiteUrl);&lt;br /&gt;              var storageAdminPage = navigator.Open&amp;lt;StorageAdminPage&amp;gt;();&lt;br /&gt;&lt;br /&gt;              navigator.Navigate&amp;lt;StorageAdminPage&amp;gt;(storageAdminPage.ClickClear);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Of course it is needed to create standalone &lt;a href="http://xunitpatterns.com/Database%20Sandbox.html"&gt;Storage Sandbox&lt;/a&gt; for each developer, which runs web tests and for each continuous integration build to avoid interfering between tests.&lt;/p&gt;&lt;p&gt;Note that both &lt;a href="http://xunitpatterns.com/SUT.html"&gt;SUT&lt;/a&gt; and Storage Admin sites should be deployed and online during the test run. It is very convenient to setup virtual folders for them in IIS, because if you use ASP.NET development server, it will run each site in instance with randomly  generated port, and it is needed always fix configs for tests.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/SypBxdiC35I/AAAAAAAAFdg/r0h1oph598w/s1600-h/image%5B8%5D.png" style="text-decoration: none;"&gt;&lt;img title="SUT and admin application in IIS" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="322" alt="SUT and admin application in IIS" src="http://lh4.ggpht.com/_aKmEmrJWYOY/SypB4fU4kNI/AAAAAAAAFdk/KL45tOq6XXg/image_thumb%5B4%5D.png?imgmax=800" width="491" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample solution.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet_90.html"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet_90.html" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-2412851820964898118?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/2412851820964898118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=2412851820964898118' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2412851820964898118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2412851820964898118'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_90.html' title='Design of Selenium tests for ASP.NET: Dealing with persistence'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/SypBsz5m7fI/AAAAAAAAFdc/ECDj0oK4krQ/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-521473852710126379</id><published>2009-12-17T04:50:00.001-08:00</published><updated>2009-12-17T04:57:56.499-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Testing AJAX. Part 2. Wait for JQuery</title><content type='html'>&lt;p&gt;While testing sites with AJAX it is always hard to catch a moment when asynchronous request is finished. I &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_13.html"&gt;already described&lt;/a&gt; function which can wait for text in label. But this approach fails when it comes to more complex behavior. For example:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_aKmEmrJWYOY/Syoo77YjTII/AAAAAAAAFdM/qe5CVE8slUA/s1600-h/AjaxOnUserManagementPage%5B6%5D.png"&gt;&lt;img title="Ajax On User Management Page" 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="484" alt="Ajax On User Management Page" src="http://lh4.ggpht.com/_aKmEmrJWYOY/Syoo-l0L_HI/AAAAAAAAFdQ/-pKMX7npFMw/AjaxOnUserManagementPage_thumb%5B4%5D.png?imgmax=800" width="502" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;On the picture above use case to add users is shown. When user clicks on add button, the system sends data to server and updates grid with users.&lt;/p&gt;  &lt;pre class="js" name="code"&gt;HomeMediator.prototype.AddUser = function(name, password)&lt;br /&gt;{&lt;br /&gt;  var me = this;&lt;br /&gt;&lt;br /&gt;  this.services.AddUser(name, password,&lt;br /&gt;      function (msg) {&lt;br /&gt;          if(msg.d.Success)&lt;br /&gt;          {&lt;br /&gt;              me.BindUsers();&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              me.addUserWidget.SetError(msg.d.Message);            &lt;br /&gt;          }&lt;br /&gt;      });&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;HomeMediator.prototype.BindUsers = function()&lt;br /&gt;{&lt;br /&gt;  var me = this;&lt;br /&gt;&lt;br /&gt;  this.services.GetAllUsers(function (users) {&lt;br /&gt;        me.userListWidget.Render(users.d);&lt;br /&gt;  });  &lt;br /&gt;};&lt;/pre&gt;&lt;p&gt;Function which executes asynchronous request on server is implemented using &lt;a href="http://jquery.com/"&gt;JQuery&lt;/a&gt; “ajax” function and  luckily it has global variable “active” which is set to true until request is done. It is possible to use this variable to force tests to wait AJAX before verification. &lt;/p&gt;&lt;pre class="csharp" name="code"&gt; public void ClickAndWaitForJQuery(Action action)&lt;br /&gt;      {&lt;br /&gt;          action();&lt;br /&gt;          WaitForJQuery();&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt; public void WaitForJQuery()&lt;br /&gt;      {&lt;br /&gt;          _selenium.WaitForCondition("selenium.browserbot.getCurrentWindow().jQuery.active == 0;", Timeout);&lt;br /&gt;      }&lt;/pre&gt;Example of usage in tests:&lt;br /&gt;&lt;pre class="csharp" name="code"&gt; public HomePageFlow ClickOnAddUser()&lt;br /&gt;      {&lt;br /&gt;          Navigator.ClickAndWaitForJQuery(_home.ClickAddUser);&lt;br /&gt;&lt;br /&gt;          return this;&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;All sources are available in &lt;a style="color: rgb(153,153,153); text-decoration: none" href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt;sample solution.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-521473852710126379?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/521473852710126379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=521473852710126379' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/521473852710126379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/521473852710126379'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_17.html' title='Design of Selenium tests for ASP.NET: Testing AJAX. Part 2. Wait for JQuery'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/Syoo-l0L_HI/AAAAAAAAFdQ/-pKMX7npFMw/s72-c/AjaxOnUserManagementPage_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-7649585531354235471</id><published>2009-12-16T06:44:00.001-08:00</published><updated>2009-12-17T04:12:11.572-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Autogenerate Page Objects for Selenium tests with T4 templates in .NET. Part 2</title><content type='html'>&lt;p&gt;In &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet.html"&gt;previous post&lt;/a&gt; it was described how to generate Page Objects for Selenium test on example with simple page. But what to do with more complex cases, when there are a lot of elements with identical ids or it is not possible to include information about type in id of control?&lt;/p&gt;  &lt;p&gt;Lets consider page to manage users from &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project:&lt;/p&gt;  &lt;p&gt; &lt;img title="image_thumb3" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="343" alt="image_thumb3" src="http://lh6.ggpht.com/_aKmEmrJWYOY/Syjyvy9C5VI/AAAAAAAAFc4/70SMVr1i3Xc/image_thumb3%5B5%5D.png?imgmax=800" width="221" border="0" /&gt;&lt;/p&gt;  &lt;p&gt;It contains a form to insert users in system and also list of already inserted users. The main problem is to get values from table – they are not wrapped in any container and therefore do not have any ids. It is only possible to access them by XPath:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_aKmEmrJWYOY/Syj6LM0jbrI/AAAAAAAAFdA/F_UiL6g3x5s/s1600-h/Untitled.png" style="text-decoration: none;"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 345px;" src="http://2.bp.blogspot.com/_aKmEmrJWYOY/Syj6LM0jbrI/AAAAAAAAFdA/F_UiL6g3x5s/s400/Untitled.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5415853622240177842" /&gt;&lt;/a&gt;&lt;p&gt; &lt;/p&gt;  &lt;p&gt;Take a look at tr elements, which are rounded by red square. It is only needed to change number next to “td” element and we will get XPath for each element in table. &lt;/p&gt;  &lt;p&gt;So, to generate code to access table we need:&lt;/p&gt;  &lt;p&gt;1) Xpathes to all elements in one grid:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[1]   &lt;br /&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[2]&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;2) Pattern to identify place where to change row number:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;table/tbody/tr[{0}]/td&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;3) Xpath to identify count of items in grid:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;4) Give meaningful names for each element in row:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[1] &lt;/em&gt;&lt;em&gt;&lt;strong&gt;{Name=lblUser}   &lt;br /&gt;&lt;/strong&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[2] &lt;strong&gt;{Name=lblPassword}&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Now id is needed to give this information to generator:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;&amp;lt;#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #&amp;gt;&lt;br /&gt;&amp;lt;#@ output extension=".cs" #&amp;gt;&lt;br /&gt;&amp;lt;#@ include file="PageObjectGeneratorByXPath.ttinclude" #&amp;gt;&lt;br /&gt;&amp;lt;#&lt;br /&gt;string url = "Home.aspx";&lt;br /&gt;&lt;br /&gt;var mainClass = new ClassInfo {&lt;br /&gt; ClassName = "Home",&lt;br /&gt; Elements =&lt;br /&gt;@"/html/body/form[@id='form1']/div[3]/span[@id='lblUserName']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/input[@id='btnLogout']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[3]/td[2]/input[@id='txtName']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[4]/td[2]/input[@id='txtPassword']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[5]/td/input[@id='btnAddUser']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[2]/td/span[@id='lblError']"};&lt;br /&gt;var rowClasses = new List&amp;lt;ClassRowInfo&amp;gt;();&lt;br /&gt;&lt;br /&gt;rowClasses.Add(&lt;br /&gt; new ClassRowInfo {&lt;br /&gt;  ClassName = "UserTable",&lt;br /&gt;  CountXpath = "/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr",&lt;br /&gt;  IteratorPattern = "tbody/tr[{0}]/td",&lt;br /&gt;  Elements =&lt;br /&gt;@"/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[1] {Name=lblUser}&lt;br /&gt;/html/body/form[@id='form1']/div[3]/div[@id='holderUsers']/table/tbody/tr[1]/td[2] {Name=lblPassword}"&lt;br /&gt; });&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;#&lt;br /&gt;RenderClasses( mainClass, rowClasses, url);&lt;br /&gt;#&amp;gt;&lt;/pre&gt;&lt;p&gt;And from this point we can access values in rows using generated Page Object in the following way:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;public HomePageFlow AssertThatUserListContains(string name, string password)&lt;br /&gt;   {&lt;br /&gt;       var isExists = false;&lt;br /&gt;&lt;br /&gt;       for (var i = 1; i &amp;lt;= _home.GetUserTableRowsCount(); i++)&lt;br /&gt;       {&lt;br /&gt;           var row = _home.GetUserTableRow(i);&lt;br /&gt;&lt;br /&gt;           if(row.User.GetText() == name &amp;amp;&amp;amp; row.Password.GetText() == password)&lt;br /&gt;           {&lt;br /&gt;               isExists = true;&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       Assert.That(isExists, Is.EqualTo(true), string.Format("User with name '{0}' and password '{1}' doesn't exists in user list table", name, password));&lt;br /&gt;&lt;br /&gt;       return this;&lt;br /&gt;   }&lt;/pre&gt;&lt;p&gt;All examples from article can be found in the &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-7649585531354235471?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/7649585531354235471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=7649585531354235471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/7649585531354235471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/7649585531354235471'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_1303.html' title='Design of Selenium tests for ASP.NET: Autogenerate Page Objects for Selenium tests with T4 templates in .NET. Part 2'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_aKmEmrJWYOY/Syjyvy9C5VI/AAAAAAAAFc4/70SMVr1i3Xc/s72-c/image_thumb3%5B5%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1167101397514321101</id><published>2009-12-16T04:40:00.001-08:00</published><updated>2009-12-17T04:12:11.572-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Meaningful failure messages</title><content type='html'>&lt;p&gt;It is not enough to create unit/acceptance tests and automate them. It is also needed to make them help you to discover issues as fast as it possible. For example lets look at the next test results:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjUwtIk8tI/AAAAAAAAFcA/kEsnHHLabZE/s1600-h/image%5B8%5D.png"&gt;&lt;img title="Selenium test failed with non informative message." 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="439" alt="Selenium test failed with non informative message." src="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjU0KPCsfI/AAAAAAAAFcE/ofUgicKFI-c/image_thumb%5B4%5D.png?imgmax=800" width="600" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Can you quickly identify the problem? Yes, something is not working, control to enter user name was not found! But why? &lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;May be application threw an unhandled exception and we got page with error screen? &lt;/li&gt;    &lt;li&gt;Login was just unsuccessful and script stayed on login.aspx page, where there is no any controls to add user of course? &lt;/li&gt;    &lt;li&gt;May be someone changed ids or markup on home.aspx and Selenium can’t find controls to enter values? &lt;/li&gt;    &lt;li&gt;Script was simply redirected on  some random page after login? &lt;/li&gt;    &lt;li&gt;May be test is just incorrect? &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Where exactly problem is? &lt;/p&gt;  &lt;p&gt;Of course it is possible to run this test again in slow mode and see what happened, but it takes a lot of time and pretty not effective.&lt;/p&gt;  &lt;p&gt;The better approach – identify issue in tests and display in error message. First lets handle 2 and 4 case – just check current page location after each action. We already &lt;a href="http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html"&gt;introduced Navigator pattern&lt;/a&gt; for this purposes – so lets implement it:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;&lt;br /&gt;public TT Navigate&amp;lt;tt&amp;gt;(Action action, bool chooseOkOnConfirmation) where TT : PageBase, new()&lt;br /&gt;      {&lt;br /&gt;          var target = new TT();&lt;br /&gt;          InitPage(target);&lt;br /&gt;&lt;br /&gt;          action();&lt;br /&gt;&lt;br /&gt;          WaitLoad(target);&lt;br /&gt;&lt;br /&gt;          AssertCorrectPageLoaded(target);&lt;br /&gt;&lt;br /&gt;          return target;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;private void AssertCorrectPageLoaded&amp;lt;TT&amp;gt;(TT target) where TT : PageBase&lt;br /&gt;      {&lt;br /&gt;          var location = _selenium.GetLocation();&lt;br /&gt;          var paramsStart = location.IndexOf('?');&lt;br /&gt;&lt;br /&gt;          if (paramsStart &amp;gt;= 0)&lt;br /&gt;          {&lt;br /&gt;              location = location.Substring(0, paramsStart);&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          if (!location.EndsWith(target.PageUrl))&lt;br /&gt;          {&lt;br /&gt;              Assert.Fail("Expected URL {0} but was {1}", target.PageUrl, location);&lt;br /&gt;          }&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;Now the same test run will give us following results:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/SyjU3fJAgCI/AAAAAAAAFcI/S9Te3R9iXgs/s1600-h/image%5B20%5D.png"&gt;&lt;img title="Selenium test failed and shows why." 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="427" alt="Selenium test failed and shows why." src="http://lh5.ggpht.com/_aKmEmrJWYOY/SyjU6E_YiVI/AAAAAAAAFcM/s8i_gZ--91Q/image_thumb%5B14%5D.png?imgmax=800" width="581" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Aha! Much better! This is definitely second case. But it is still not clear what happened? Let’s look for standard ASP.NET error  message and display it:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt; public TT Navigate&amp;lt;TT&amp;gt;(Action action, bool chooseOkOnConfirmation) where TT : PageBase, new()&lt;br /&gt;      {&lt;br /&gt;          var target = new TT();&lt;br /&gt;          InitPage(target);&lt;br /&gt;&lt;br /&gt;          action();&lt;br /&gt;&lt;br /&gt;          WaitLoad(target);&lt;br /&gt;&lt;br /&gt;          if (target.Selenium.GetBodyText().Contains("Server Error in "))&lt;br /&gt;          {&lt;br /&gt;              Assert.Fail(String.Format("Server error while navigating\r\n\r\n {0}.", target.Selenium.GetBodyText()));&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          AssertCorrectPageLoaded(target);&lt;br /&gt;&lt;br /&gt;          return target;&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;Lets look now:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/SyjU9VTgmRI/AAAAAAAAFcQ/f-0Fk6sOmOM/s1600-h/image%5B34%5D.png"&gt;&lt;br /&gt;&lt;img title="Selenium test failed and shows ASP.NET error message" 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="705" alt="Selenium test failed and shows ASP.NET error message" src="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjVBkFs3MI/AAAAAAAAFcU/DWPQ-OoguO0/image_thumb%5B26%5D.png?imgmax=800" width="593" border="0" /&gt;&lt;/a&gt; Here we go… The problem is that application can not find file with user definitions. It is needed to fix configuration file a little bit to resolve this issue. It will take no more then 1 minute to identify where the problem is, fix it and rerun test. Pretty effective, is not it?&lt;/p&gt;&lt;p&gt;Also do not forget to add meaningful messages to asserts, which check business cases. &lt;/p&gt;&lt;p&gt;Assert without message:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjVEjGIpfI/AAAAAAAAFcY/x1C3YELmIfU/s1600-h/image%5B47%5D.png"&gt;&lt;br /&gt;&lt;img title="Assert without message" 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="426" alt="Assert without message" src="http://lh5.ggpht.com/_aKmEmrJWYOY/SyjVHmje5WI/AAAAAAAAFcc/TIHw3BRRBy0/image_thumb%5B35%5D.png?imgmax=800" width="615" border="0" /&gt;&lt;/a&gt; Assert with message:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjVKf7FLrI/AAAAAAAAFcg/rKRXhxg4Cls/s1600-h/image%5B40%5D.png"&gt;&lt;br /&gt;&lt;img title="Assert with message" 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="427" alt="Assert with message" src="http://lh3.ggpht.com/_aKmEmrJWYOY/SyjVNPMiMhI/AAAAAAAAFck/cPxLwFJTUNM/image_thumb%5B30%5D.png?imgmax=800" width="620" border="0" /&gt;&lt;/a&gt; Understanding of what happened is dramatically changed by only one simple message in assert:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt; public HomePageFlow AssertThatUserListContains(string name, string password)&lt;br /&gt;      {&lt;br /&gt;          var isExists = false;&lt;br /&gt;&lt;br /&gt;          for (var i = 1; i &amp;lt;= _home.GetUserTableRowsCount(); i++)&lt;br /&gt;          {&lt;br /&gt;              var row = _home.GetUserTableRow(i);&lt;br /&gt;&lt;br /&gt;              if(row.User.GetText() == name &amp;amp;&amp;amp; row.Password.GetText() == password)&lt;br /&gt;              {&lt;br /&gt;                  isExists = true;&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          Assert.That(isExists, Is.EqualTo(true), string.Format("User with name '{0}' and password '{1}' doesn't exists in user list table", name, password));&lt;br /&gt;&lt;br /&gt;          return this;&lt;br /&gt;      }&lt;/pre&gt;&lt;p&gt;All sources from this article can be found in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1167101397514321101?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1167101397514321101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1167101397514321101' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1167101397514321101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1167101397514321101'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_16.html' title='Design of Selenium tests for ASP.NET: Meaningful failure messages'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_aKmEmrJWYOY/SyjU0KPCsfI/AAAAAAAAFcE/ofUgicKFI-c/s72-c/image_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-4732873450439556327</id><published>2009-12-13T02:51:00.001-08:00</published><updated>2009-12-17T04:12:11.573-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Testing AJAX</title><content type='html'>&lt;p&gt;When it comes to AJAX, UI tests started to behave unexpectedly. Sometimes they fail, sometimes pass:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyTHEIQSUBI/AAAAAAAAFbE/lyse88DH_B8/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="AJAX request on server" border="0" alt="AJAX request on server" src="http://lh4.ggpht.com/_aKmEmrJWYOY/SyTHFetqucI/AAAAAAAAFbI/98bEe4hEFH4/image_thumb%5B9%5D.png?imgmax=800" width="826" height="408" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_aKmEmrJWYOY/SyTHGXJNFEI/AAAAAAAAFbM/IE-_VMoqkhE/s1600-h/image%5B11%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Selenium tests failed when page has AJAX requests." border="0" alt="Selenium tests failed when page has AJAX requests." src="http://lh5.ggpht.com/_aKmEmrJWYOY/SyTHH0QZHYI/AAAAAAAAFbQ/k36mNPzsHpw/image_thumb%5B5%5D.png?imgmax=800" width="659" height="495" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The main trick in testing AJAX is to wait until asynchronous request is completed. There is an universal waitForCondition function which gets JavaScript Boolean expression as argument, and waits until this expression returns true.&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;public HomePageFlow AssertUserName(string userName)&lt;br /&gt;{&lt;br /&gt;   Navigator.WaitForText(_home.UserName.Selector);&lt;br /&gt;&lt;br /&gt;   Assert.That(_home.UserName.GetText(), Is.EqualTo(userName));&lt;br /&gt;&lt;br /&gt;   return this;&lt;br /&gt;}&lt;/pre&gt;&lt;pre class="csharp" name="code"&gt;public void WaitForText(string selector)&lt;br /&gt; {&lt;br /&gt;     selector = selector.Replace(@"'", @"\'");&lt;br /&gt;&lt;br /&gt;     string script = string.Format("var value = selenium.getText('{0}'); value.length &amp;gt; 0;", selector);&lt;br /&gt;&lt;br /&gt;     _selenium.WaitForCondition(script, Timeout);&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;It was needed to change only Flows to tell them about AJAX behavior. It is not needed to change  any tests, and this is reasonable – because actually nothing was changed from user and UI tests prospective in UI. This means that correct level of abstraction was selected.&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;using NUnit.Framework;&lt;br /&gt;using Tests.SmokeTest.Core;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.Tests&lt;br /&gt;{&lt;br /&gt;[TestFixture]&lt;br /&gt;public class HomeTest : TestBase&lt;br /&gt;{&lt;br /&gt;[Test]&lt;br /&gt;public void CheckCurrentUserName()&lt;br /&gt;{&lt;br /&gt;   Start&lt;br /&gt;       .LoginAndGoToHomePage()&lt;br /&gt;       .AssertUserName("admin");&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;All sources are available in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample solution.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet_13.html"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet_13.html" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-4732873450439556327?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/4732873450439556327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=4732873450439556327' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/4732873450439556327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/4732873450439556327'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_13.html' title='Design of Selenium tests for ASP.NET: Testing AJAX'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_aKmEmrJWYOY/SyTHFetqucI/AAAAAAAAFbI/98bEe4hEFH4/s72-c/image_thumb%5B9%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-9219669513370870471</id><published>2009-12-11T11:42:00.000-08:00</published><updated>2009-12-17T04:12:11.573-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Integrate Selenium server in Visual studio</title><content type='html'>It is very useful to run selenium in Visual Studio. All settings are on the picture below:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aKmEmrJWYOY/SyKizXuRlAI/AAAAAAAAFa8/WwjrRm-ka9E/s1600-h/IntegrateSelenuimInVS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://3.bp.blogspot.com/_aKmEmrJWYOY/SyKizXuRlAI/AAAAAAAAFa8/WwjrRm-ka9E/s400/IntegrateSelenuimInVS.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5414068705477891074" /&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="color:#0000EE;"&gt;&lt;span class="Apple-style-span" style=""&gt;&lt;span class="Apple-style-span"  style="color:#000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;1) Open Visual Studio&lt;br /&gt;2) On the Tools menu, choose External Tools.&lt;br /&gt;3) In the External Tools dialog box, choose Add, and enter a name "Selenium Server" in the Title box.&lt;br /&gt;4) In the Command box, enter the path to the Java Runtime. For example "C:\Program Files\Java\jre6\bin\java.exe"&lt;br /&gt;5) In the Arguments box, enter arguments in the following format - "-jar selenium-server.jar".&lt;br /&gt;6) In the Initial directory, enter "$(SolutionDir)\_tools\selenium"&lt;br /&gt;7) Select Use output window and then choose OK.&lt;br /&gt;&lt;br /&gt;Place selenium-server.jar file in $(SolutionDir)\_tools\selenium directory, commit it in SVN, so every developer will have the same version of Selenium as you.&lt;br /&gt;&lt;br /&gt;Now server can be started/stopped through "Tools" menu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-9219669513370870471?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/9219669513370870471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=9219669513370870471' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/9219669513370870471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/9219669513370870471'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/integrate-selenium-server-in-visual.html' title='Integrate Selenium server in Visual studio'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_aKmEmrJWYOY/SyKizXuRlAI/AAAAAAAAFa8/WwjrRm-ka9E/s72-c/IntegrateSelenuimInVS.jpg' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-8968388326985777712</id><published>2009-12-09T06:23:00.001-08:00</published><updated>2009-12-17T04:12:11.574-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Introducing Flow pattern</title><content type='html'>&lt;p&gt;While acceptance testing through UI it is impossible to test only one aspect of behavior  of system like we do usually in unit tests. Acceptance tests asserts use cases, which can be composed of a number of other use cases. One of the frequently encountered examples is login procedure. It is always needed to login first before composing e-mail, see list of incoming messages or to be able to see user specific information.&lt;/p&gt;  &lt;p&gt;Flow pattern is intended to encapsulate one use case. It can simply fill two fields:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;public LoginPageFlow EnterCredentials(string name, string password)&lt;br /&gt;{&lt;br /&gt; _login.User.SetText(name);&lt;br /&gt; _login.Password.SetText(password);&lt;br /&gt;&lt;br /&gt; return this;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Or be  composed from a number of another complex use cases.&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;public HomePageFlow LoginAndGoToHomePage()&lt;br /&gt;{&lt;br /&gt; return GoToLoginPage()&lt;br /&gt;  .EnterValidCredentials()&lt;br /&gt;  .Login();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Usually it is implemented using &lt;a href="http://martinfowler.com/bliki/FluentInterface.html"&gt;Fluent Interface&lt;/a&gt; pattern – this makes consumer code much more clear. Combining different flows it is possible to test something very carefully:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;using NUnit.Framework;&lt;br /&gt;using Tests.SmokeTest.Core;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.Tests&lt;br /&gt;{&lt;br /&gt;   [TestFixture]&lt;br /&gt;   public class LoginTest : TestBase&lt;br /&gt;   {&lt;br /&gt;       [Test]&lt;br /&gt;       public void LoginSuccessWithCorrectPassword()&lt;br /&gt;       {&lt;br /&gt;           Start&lt;br /&gt;               .GoToLoginPage()&lt;br /&gt;               .EnterCredentials("admin", "god")&lt;br /&gt;               .Login();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       [Test]&lt;br /&gt;       public void LoginWrongWithWrongPassword()&lt;br /&gt;       {&lt;br /&gt;           Start&lt;br /&gt;               .GoToLoginPage()&lt;br /&gt;               .EnterCredentials("admin", "incorrectPwd")&lt;br /&gt;               .LoginShouldFail()&lt;br /&gt;               .AssertMessage("Username and password do not match.");&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Or hide from reader some unimportant steps (login procedure in this case)&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;using NUnit.Framework;&lt;br /&gt;using Tests.SmokeTest.Core;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.Tests&lt;br /&gt;{&lt;br /&gt;   [TestFixture]&lt;br /&gt;   public class HomeTest : TestBase&lt;br /&gt;   {&lt;br /&gt;       [Test]&lt;br /&gt;       public void CheckCurrentUserName()&lt;br /&gt;       {&lt;br /&gt;           Start&lt;br /&gt;               .LoginAndGoToHomePage()&lt;br /&gt;               .AssertUserName("admin");&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;One more benefit of Flow pattern is that tests became more declarative, and now it is very easy to extract &lt;a href="http://www.martinfowler.com/bliki/DomainSpecificLanguage.html"&gt;DSL (domain specific language)&lt;/a&gt;, which can be used by tester or customer for creating new tests. But this is the topic for another post.&lt;/p&gt;&lt;p&gt;All sources from this post can be found in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-8968388326985777712?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/8968388326985777712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=8968388326985777712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8968388326985777712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8968388326985777712'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_09.html' title='Design of Selenium tests for ASP.NET: Introducing Flow pattern'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1668312695281886388</id><published>2009-12-09T05:27:00.001-08:00</published><updated>2009-12-17T04:12:11.574-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Autogenerate Page Objects for Selenium tests with T4 templates in .NET</title><content type='html'>&lt;p&gt;In my previous post &lt;a href="http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html"&gt;Design of Selenium tests for ASP.NET: Page Object and Navigator patterns&lt;/a&gt; i described &lt;a href="http://code.google.com/p/webdriver/wiki/PageObjects"&gt;Page Object&lt;/a&gt; pattern which helps to improve maintainability of tests through UI. But to maintain these Page Object is still great effort… And good news, everybody! We can easily autogenerate them! How?&lt;/p&gt;  &lt;p&gt;Firstly we need a list of all important controls on page. Usually they are inputs and links, but also spans, headers, lists… whatever. Plug-in &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1192"&gt;Xpather&lt;/a&gt; for Firefox can easily find them all). Just type “//input | //span” in “XPath” textbox, select needed controls and copy list of XPaths from “XPaths” tab:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_aKmEmrJWYOY/Sx-lt7wSgmI/AAAAAAAAFZQ/CvCYIbWXvrc/s1600-h/Xpather%5B6%5D.png"&gt;&lt;img title="Xpather" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="584" alt="Xpather" src="http://lh5.ggpht.com/_aKmEmrJWYOY/Sx-lzPLBUMI/AAAAAAAAFZc/sFDfl_9UEEg/Xpather_thumb%5B4%5D.png?imgmax=800" width="675" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You will get list like this:&lt;/p&gt;  &lt;p&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[1]/td/span[@id='lblMessage']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[2]/td[2]/input[@id='txtUser'] &lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[3]/td[2]/input[@id='txtPassword'] &lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[4]/td/input[@id='btnLogin']&lt;/p&gt;  &lt;p&gt;Look at these XPathes – it is possible to extract all information that needed to identify type and name of each control. Also, as you can see i added prefixes to control names in order to recognize whether it is button or textbox. If yours style guidelines are prohibit such names, you can find another workaround – for example add some attributes manually after gaining them from XPather.&lt;/p&gt;  &lt;p&gt;The best way to autogenerate code in Visual Studio is &lt;a href="http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx"&gt;T4 templates&lt;/a&gt;. It is needed to create simple transformation and insert our Xpathes in it:&lt;/p&gt;  &lt;pre class="csharp" name="code"&gt;&amp;lt;#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #&amp;gt;&lt;br /&gt;&amp;lt;#@ output extension=".cs" #&amp;gt;&lt;br /&gt;&amp;lt;#@ include file="PageObjectGeneratorByXPath.ttinclude" #&amp;gt;&lt;br /&gt;&amp;lt;#&lt;br /&gt;string url = "Login.aspx";&lt;br /&gt;&lt;br /&gt;var mainClass = new ClassInfo {&lt;br /&gt;ClassName = "Login",&lt;br /&gt;Elements =&lt;br /&gt;@"/html/body/form[@id='form1']/div[3]/table/tbody/tr[1]/td/span[@id='lblMessage']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[2]/td[2]/input[@id='txtUser']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[3]/td[2]/input[@id='txtPassword']&lt;br /&gt;/html/body/form[@id='form1']/div[3]/table/tbody/tr[4]/td/input[@id='btnLogin']"};&lt;br /&gt;var rowClasses = new List&amp;lt;ClassRowInfo&amp;gt;();&lt;br /&gt;#&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;#&lt;br /&gt;RenderClasses( mainClass, rowClasses, url);&lt;br /&gt;#&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Implementation of RenderClasses function can be found in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample solution in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/Tests.SmokeTest/PageObjects/Templates/LoginPage.tt"&gt;LoginPage Page Object template&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This template will autogenerate next ready to use code:&lt;/p&gt;&lt;pre class="csharp" name="code"&gt;using Tests.SmokeTest.Core;&lt;br /&gt;using Tests.SmokeTest.PageObjects.Controls;&lt;br /&gt;using Selenium;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.PageObjects&lt;br /&gt;{&lt;br /&gt;public partial class LoginPage : PageBase&lt;br /&gt;{&lt;br /&gt;    public LoginPage() : base("Login.aspx") { }&lt;br /&gt;&lt;br /&gt;    private Label _message = null;&lt;br /&gt;    public Label Message&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            if (_message == null)&lt;br /&gt;            {&lt;br /&gt;                _message = new Label(Selenium, @"xpath=/html/body/form[@id='form1']/div[3]/table/tbody/tr[1]/td/span[@id='lblMessage']");&lt;br /&gt;            }&lt;br /&gt;            return _message;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    private TextField _user = null;&lt;br /&gt;    public TextField User&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            if (_user == null)&lt;br /&gt;            {&lt;br /&gt;                _user = new TextField(Selenium, @"xpath=/html/body/form[@id='form1']/div[3]/table/tbody/tr[2]/td[2]/input[@id='txtUser']");&lt;br /&gt;            }&lt;br /&gt;            return _user;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    private TextField _password = null;&lt;br /&gt;    public TextField Password&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            if (_password == null)&lt;br /&gt;            {&lt;br /&gt;                _password = new TextField(Selenium, @"xpath=/html/body/form[@id='form1']/div[3]/table/tbody/tr[3]/td[2]/input[@id='txtPassword']");&lt;br /&gt;            }&lt;br /&gt;            return _password;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public void ClickLogin()&lt;br /&gt;    {&lt;br /&gt;        Selenium.Click(@"xpath=/html/body/form[@id='form1']/div[3]/table/tbody/tr[4]/td/input[@id='btnLogin']");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;What benefits provides autogeneration of Page Objects? first of all it is not longer needed to painstakingly extract these ids from html and hardcode them into code. Secondly, if page is changed, then autogenerated Page Object will contain another set of properties and its types, and this will cause errors in compile time. This errors are pretty easy to fix unlike to runtime errors with hardcoded IDs or Xpathes.&lt;/p&gt;&lt;p&gt;To get know how code generation works for pages with grids and for controls without id please refer &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_1303.html"&gt;part 2&lt;/a&gt; article.&lt;/p&gt;&lt;p&gt;Next post will describe &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_09.html"&gt;how to improve readability of tests using Flow pattern&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet.html"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f12%2fdesign-of-selenium-tests-for-aspnet.html" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1668312695281886388?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1668312695281886388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1668312695281886388' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1668312695281886388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1668312695281886388'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet.html' title='Design of Selenium tests for ASP.NET: Autogenerate Page Objects for Selenium tests with T4 templates in .NET'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_aKmEmrJWYOY/Sx-lzPLBUMI/AAAAAAAAFZc/sFDfl_9UEEg/s72-c/Xpather_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-2637915164598723952</id><published>2009-11-26T12:33:00.001-08:00</published><updated>2009-12-17T04:12:11.574-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Tests'/><title type='text'>Design of Selenium tests for ASP.NET: Page Object and Navigator patterns</title><content type='html'>&lt;p&gt;&lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; is great tool for acceptance testing of web applications, but using it in “raw” style (record tests, fix recorded script and playback) causes a lot of drawbacks in future. It is just impossible to maintain such tests. Therefore i would like to share my and &lt;a href="http://www.linkedin.com/pub/valera-kolupaev/4/8a6/586"&gt;Valera’s&lt;/a&gt; experience of implementing acceptance tests through UI. &lt;/p&gt;  &lt;p&gt;Along with writing posts i will implements all i described in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; project, so you can find all samples there.&lt;/p&gt;  &lt;p&gt;First two things which are needed to be introduced are Page Object and Navigator patterns:&lt;/p&gt;  &lt;h4&gt;Page Object&lt;/h4&gt;  &lt;p&gt;&lt;a href="http://code.google.com/p/webdriver/wiki/PageObjects"&gt;Page Objects&lt;/a&gt; is “must have” pattern in automated testing through UI. Without it tests becomes too dependent from html structure, developers have to fix UI tests after each UI change. Imaging how frustrating to fix all 100 test cases in Smoke test, after inserting div around login form (All tests cases included login procedure of course)! &lt;/p&gt;  &lt;p&gt;The idea of Page Object is simple - Encapsulate all calls to controls on page:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_aKmEmrJWYOY/Sw-ykAG6d5I/AAAAAAAAFY4/pYQk-hxLS-k/s1600-h/LoginPageObject%5B5%5D.png"&gt;&lt;img title="LoginPageObject" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="231" alt="LoginPageObject" src="http://lh3.ggpht.com/_aKmEmrJWYOY/Sw-ylZraTTI/AAAAAAAAFY8/fl07B4ta5g4/LoginPageObject_thumb%5B3%5D.png?imgmax=800" width="683" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;And it contain only methods and properties, which delegate calls to Selenium framework using hardcoded IDs, or xPathes: &lt;/p&gt;  &lt;pre class="c#" name="code"&gt;namespace Infrastructure.Tests.PageObjects&lt;br /&gt;{&lt;br /&gt;public class LoginPage : PageBase&lt;br /&gt;{&lt;br /&gt;  public LoginPage() : base("Login.aspx")&lt;br /&gt;  { }&lt;br /&gt;&lt;br /&gt;  public void LoginButtonClick()&lt;br /&gt;  {&lt;br /&gt;      Selenium.Click("btnLogin");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string ErrorMessage&lt;br /&gt;  {&lt;br /&gt;      get { return Selenium.GetText("lbMessage"); }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string Password&lt;br /&gt;  {&lt;br /&gt;      set { Selenium.Type("tbxPassword", value); }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string UserName&lt;br /&gt;  {&lt;br /&gt;      set { Selenium.Type("tbxLogin", value); }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;All test cases should use Page Objects to access controls on page. If something will be changed in html, it will be needed to change only Page Objects to fix all test cases.&lt;/p&gt;Example of Page Object pattern in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project is located &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/Tests.SmokeTest/PageObjects/Templates/HomePage.cs"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-weight: bold"&gt;Navigator&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Navigator represents entity, which monitors what page resides in browser. All redirects, events which can causes redirects, postbacks, popups, ajax calls should be performed using Navigator. Responsibilities of Navigator includes:&lt;br /&gt;&lt;div&gt;&lt;ol&gt;    &lt;li&gt;Assert that redirect was performed to the right place. It means after click on “Home” link in menu user must be redirected to HomePage, not to Login or somewhere else.&lt;br /&gt;  &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Assert that redirect or postback is not caused server error and if caused, then gather information about error and include it in the error message of failed test.&lt;br /&gt;  &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Prevent appearing of confusing errors like “Control with ID ‘bla-bla-bla’ was not found”. Often tests includes scenarios when it is needed to be redirected on some page and find control on it. But if page throws exception, there will be no controls on it – just error message.&lt;br /&gt;  &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Encapsulate all “wait” activities. It should wait for browser to load whole page before accessing it, wait for completing AJAX requests and of course manage timeouts.&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;Interface of Navigator:&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;using System;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.Core&lt;br /&gt;{&lt;br /&gt;public interface INavigator&lt;br /&gt;{&lt;br /&gt;  TT Open&amp;lt;tt&amp;gt;() where TT : PageBase, new();&lt;br /&gt;  TT Navigate&amp;lt;tt&amp;gt;(Action action) where TT : PageBase, new();&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;  &lt;p&gt;Usage of Navigator:&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;var login = _navigator.Open&amp;lt;loginpage&amp;gt;();&lt;br /&gt;&lt;br /&gt;login.User.SetText("admin");&lt;br /&gt;login.Password.SetText("incorrectPwd");&lt;br /&gt;&lt;br /&gt;login = _navigator.Navigate&amp;lt;loginpage&amp;gt;(login.ClickLogin);&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;  &lt;p&gt;Example of Navigator pattern in &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/"&gt;design-of-selenium-tests-for-asp-net&lt;/a&gt; sample project is located &lt;a href="http://code.google.com/p/design-of-selenium-tests-for-asp-net/source/browse/trunk/Tests.SmokeTest/Core/Navigator.cs"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;After introducing this patterns tests becomes clear, easy to read and easy to implement. Just check it out:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;using NUnit.Framework;&lt;br /&gt;using Tests.SmokeTest.Core;&lt;br /&gt;using Tests.SmokeTest.PageObjects;&lt;br /&gt;&lt;br /&gt;namespace Tests.SmokeTest.Tests&lt;br /&gt;{&lt;br /&gt;  [TestFixture]&lt;br /&gt;  public class LoginTest&lt;br /&gt;  {&lt;br /&gt;      private SeleniumScope _scope;&lt;br /&gt;      private INavigator _navigator;&lt;br /&gt;&lt;br /&gt;      [TestFixtureSetUp]&lt;br /&gt;      public void Setup()&lt;br /&gt;      {&lt;br /&gt;          _scope = new SeleniumScope();&lt;br /&gt;          _navigator = new Navigator(_scope.Selenium);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      [TestFixtureTearDown]&lt;br /&gt;      public void TearDown()&lt;br /&gt;      {&lt;br /&gt;          _scope.Dispose();&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      [Test]&lt;br /&gt;      public void LoginSuccessWithCorrectPassword()&lt;br /&gt;      {&lt;br /&gt;          var login = _navigator.Open&amp;lt;LoginPage&amp;gt;();&lt;br /&gt;&lt;br /&gt;          login.User.SetText("admin");&lt;br /&gt;          login.Password.SetText("god");&lt;br /&gt;&lt;br /&gt;          _navigator.Navigate&amp;lt;HomePage&amp;gt;(login.ClickLogin);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      [Test]&lt;br /&gt;      public void LoginWrongWithWrongPassword()&lt;br /&gt;      {&lt;br /&gt;          var login = _navigator.Open&amp;lt;LoginPage&amp;gt;();&lt;br /&gt;&lt;br /&gt;          login.User.SetText("admin");&lt;br /&gt;          login.Password.SetText("incorrectPwd");&lt;br /&gt;&lt;br /&gt;          login = _navigator.Navigate&amp;lt;LoginPage&amp;gt;(login.ClickLogin);&lt;br /&gt;&lt;br /&gt;          Assert.That(login.Message.GetText(), Is.EqualTo("Username and password do not match."));&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;  &lt;p&gt;Next post will describe &lt;a href="http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet.html"&gt;how to autogenerate Page Object using T4 templates&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f11%2fdesign-of-selenium-tests-for-aspnet.html"&gt;&lt;img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f11%2fdesign-of-selenium-tests-for-aspnet.html" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-2637915164598723952?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/2637915164598723952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=2637915164598723952' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2637915164598723952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2637915164598723952'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/11/design-of-selenium-tests-for-aspnet.html' title='Design of Selenium tests for ASP.NET: Page Object and Navigator patterns'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_aKmEmrJWYOY/Sw-ylZraTTI/AAAAAAAAFY8/fl07B4ta5g4/s72-c/LoginPageObject_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1929111827937290947</id><published>2009-11-20T02:50:00.001-08:00</published><updated>2009-11-20T02:51:19.875-08:00</updated><title type='text'>Tool to write posts on Blogger</title><content type='html'>&lt;p&gt;I was looking for tool, which will allow to post on Blogger not using web interface. I found &lt;a href="http://download.live.com/writer"&gt;Windows Live Writer&lt;/a&gt; and it seems that it is exactly what i needed.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1929111827937290947?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1929111827937290947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1929111827937290947' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1929111827937290947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1929111827937290947'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/11/tool-to-write-posts-on-blogger.html' title='Tool to write posts on Blogger'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-281575794056961664</id><published>2009-11-20T01:48:00.001-08:00</published><updated>2009-11-20T04:14:26.553-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>How to connect to Microsoft Analysis Services from ASP.NET</title><content type='html'>&lt;p&gt;Unfortunately there is no mixed mode security in Analysis Services like in SQL server. You can not just pass login and password through connection string. &lt;/p&gt;  &lt;p&gt;The only way to connect to Analysis Services is windows authentication, and therefore impersonalization.&lt;/p&gt;  &lt;p&gt;Here is the simple solution:&lt;/p&gt;  &lt;p&gt;1) Create the same User with the same password on both servers: on server with ASP.NET application and on server with Analysis services. For example: “AnalysisUser” and “AnalysisPassword”&lt;/p&gt;  &lt;p&gt;2) Configure access to Analysis Services and give him all rights to run ASP.NET application.&lt;/p&gt;  &lt;p&gt;3)&amp;#160; Configure impersonalization for ASP.NET application by inserting following line in Web.config in “system.web” section:&lt;/p&gt;  &lt;p&gt;&amp;lt;identity impersonate=&amp;quot;true&amp;quot; userName =&amp;quot;AnalysisUser&amp;quot; password=&amp;quot;AnalysisPassword&amp;quot;/&amp;gt;&lt;/p&gt;  &lt;p&gt;That is it, after these manipulations connection should be functional. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-281575794056961664?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/281575794056961664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=281575794056961664' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/281575794056961664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/281575794056961664'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/11/how-to-connect-to-microsoft-analysis.html' title='How to connect to Microsoft Analysis Services from ASP.NET'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1091310443127872579</id><published>2009-11-20T01:21:00.000-08:00</published><updated>2009-11-20T06:04:38.182-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP'/><title type='text'>Cumulative calculations in Microsoft Analysis services</title><content type='html'>&lt;p&gt;The simplest method to add cumulative in MDX query is calculated member&lt;/p&gt;  &lt;pre&gt;WITH MEMBER &lt;br /&gt;	[Measures].[Cumulative Amount]&lt;br /&gt;AS &lt;br /&gt;	Sum({&lt;br /&gt;			{[D Date].[Quarter End Date].CurrentMember.Level.Members}.Item(0): &lt;br /&gt;			[D Date].[Quarter End Date].CurrentMember&lt;br /&gt;		}, [Measures].[Ending Balance AMT])&lt;br /&gt;select {&lt;br /&gt;[Measures].[Ending Balance AMT],&lt;br /&gt;[Measures].[Cumulative Amount]&lt;br /&gt;} on 0 ,&lt;br /&gt;&lt;br /&gt; ({[D Date].[Quarter End Date].&amp;amp;[2002-12-31T00:00:00]:[D Date].[Quarter End Date].&amp;amp;[2003-12-31T00:00:00]}) on 1&lt;br /&gt;  from  [FundHoldings]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Will produce next result:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_aKmEmrJWYOY/Swah8Jq-xsI/AAAAAAAAFYw/tbJqMDwcto4/s1600-h/result%5B6%5D.jpg"&gt;&lt;img title="result of cumulative MDX query" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="137" alt="result of cumulative MDX query" src="http://lh3.ggpht.com/_aKmEmrJWYOY/Swah9BRETGI/AAAAAAAAFY0/4ZMjBqxLgtA/result_thumb%5B4%5D.jpg?imgmax=800" width="301" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here “[Measures].[Cumulative Amount]” is calculated member, which is sum of all values from measure “[Measures].[Ending Balance AMT]” by dimension&amp;#160; “[D Date].[Quarter End Date]”. Note that this dimension should be included into one of axes, and by this axis calculations will be executed. Be careful - More precise granularity of dimension in this axis will hit performance, because it will be needed to sum more values.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1091310443127872579?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1091310443127872579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1091310443127872579' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1091310443127872579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1091310443127872579'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/11/cumulative-calculations-in-microsoft.html' title='Cumulative calculations in Microsoft Analysis services'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_aKmEmrJWYOY/Swah9BRETGI/AAAAAAAAFY0/4ZMjBqxLgtA/s72-c/result_thumb%5B4%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-5336699815272069945</id><published>2009-08-10T12:07:00.000-07:00</published><updated>2009-08-10T12:48:16.584-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Refactoring with Visual studio macros</title><content type='html'>Visual Studio macros is a very powerful tool for refactoring. It can help even in situations where &lt;a href="http://www.jetbrains.com/resharper/index.html"&gt;Resharper&lt;/a&gt; (Favorite add-in for VS of mine) is useless.&lt;br /&gt;&lt;br /&gt;For example we have code like this: &lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;i&gt;params(1) = New SqlClient.SqlParameter("@DealID", SqlDbType.Int)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(1).Direction = ParameterDirection.Input&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(1).Value = oVOBDealFund.DEalID&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(2) = New SqlClient.SqlParameter("@FundID", SqlDbType.Int)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(2).Direction = ParameterDirection.Input&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(2).Value = oVOBDealFund.FundID&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(3) = New SqlClient.SqlParameter("@FundDate", SqlDbType.DateTime)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(3).Direction = ParameterDirection.Input&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(3).Value = CType(oVOBDealFund.FundDate, DateTime)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(4) = New SqlClient.SqlParameter("@FundFollowOn", SqlDbType.DateTime)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(4).Direction = ParameterDirection.Input&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(4).Value = oVOBDealFund.FundFollowOn&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This code is a great example of &lt;a href="http://sourcemaking.com/antipatterns/cut-and-paste-programming"&gt;Cut-And-Paste&lt;/a&gt; programming and must be avoided. After chains of refactorings with Resharper (Extract method, Make method shared, move method in another class)  we will get something like this:&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;                params(1) = Parameter.InputInt("@DealID", oVOBDealFund.DEalID)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(2) = Parameter.InputInt("@FundID", oVOBDealFund.FundID)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(3) = Parameter.InputDate("@FundDate", CType(oVOBDealFund. FundDate, DateTime))&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;                params(4) = Parameter.InputDate("@FundFollowOn", oVOBDealFund. FundFollowOn)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But is takes a lot of time to perform such refactorings for each sql parameter. Taking into account number of code lines to be refactored (on my current project there are about 400 methods in DAL written in such manner), this solution is just unacceptable.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lets write simple macros to perform refactoring of single sql parameter:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;&lt;div&gt;Sub ReplaceParameters()&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.EndOfLine()&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordLeft(False, 2)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordRight(True)&lt;/div&gt;&lt;div&gt;        Dim parameterType As String = DTE.ActiveDocument.Selection.Text&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;        Dim functionName As String&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;        If parameterType = "DateTime" Then&lt;/div&gt;&lt;div&gt;            functionName = "Parameter.InputDate"&lt;/div&gt;&lt;div&gt;        ElseIf parameterType = "Int" Then&lt;/div&gt;&lt;div&gt;            functionName = "Parameter.InputInt"&lt;/div&gt;&lt;div&gt;        ElseIf parameterType = "VarChar" Then&lt;/div&gt;&lt;div&gt;            functionName = "Parameter.InputString"&lt;/div&gt;&lt;div&gt;        ElseIf parameterType = "Money" Then&lt;/div&gt;&lt;div&gt;            functionName = "Parameter.InputMoney"&lt;/div&gt;&lt;div&gt;        End If&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordRight(False, 5)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordRight(True, 4)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.Text = functionName&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordRight(False, 4)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.LineDown(True, 2)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.WordRight(True, 7)&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.Text = ","&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.DeleteLeft()&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.EndOfLine()&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.Text = ")"&lt;/div&gt;&lt;div&gt;        DTE.ActiveDocument.Selection.LineDown(False, 2)&lt;/div&gt;&lt;div&gt;    End Sub&lt;/div&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That's it !&lt;/div&gt;&lt;div&gt;This macros will refactor single parameter in a moment. You just need to point on first line of statement and execute macros! &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f08%2frefactoring-with-visual-studio-macros.html"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f08%2frefactoring-with-visual-studio-macros.html" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-5336699815272069945?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/5336699815272069945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=5336699815272069945' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5336699815272069945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5336699815272069945'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/08/refactoring-with-visual-studio-macros.html' title='Refactoring with Visual studio macros'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-3926589884833935771</id><published>2009-08-10T11:11:00.000-07:00</published><updated>2009-11-20T04:33:40.027-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Visual Studio macros to attach to web development server</title><content type='html'>It is very boring to attach debugger to process from menu. With this macros binded on shortcut it can be done in a moment.   &lt;br /&gt;  &lt;br /&gt;  &lt;pre name="code" class="vb"&gt;  &lt;br /&gt;Sub AttachToDevServer()&lt;br /&gt;Try&lt;br /&gt;Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger&lt;br /&gt;Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")&lt;br /&gt;Dim dbgeng(3) As EnvDTE80.Engine&lt;br /&gt;dbgeng(0) = trans.Engines.Item("T-SQL")&lt;br /&gt;dbgeng(1) = trans.Engines.Item("T-SQL")&lt;br /&gt;dbgeng(2) = trans.Engines.Item("Managed")&lt;br /&gt;Dim proc2 As EnvDTE80.Process2 = dbg2.GetProcesses(trans, "localhost").Item("WebDev.WebServer.EXE")&lt;br /&gt;proc2.Attach2(dbgeng)&lt;br /&gt;Catch ex As System.Exception&lt;br /&gt;MsgBox(ex.Message)&lt;br /&gt;End Try&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-3926589884833935771?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/3926589884833935771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=3926589884833935771' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3926589884833935771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/3926589884833935771'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/08/visual-studio-macros-to-attach-to-web.html' title='Visual Studio macros to attach to web development server'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-8366245702858584693</id><published>2009-08-09T23:41:00.000-07:00</published><updated>2009-08-10T00:01:48.173-07:00</updated><title type='text'>JsInject -</title><content type='html'>A sample of JS component which was created in Visual Studio with jsTestDriver attached can be found by link : &lt;a href="http://code.google.com/p/jsinject/"&gt;JsInject&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;It was fully written by TDD and inspired by &lt;a href="http://funq.codeplex.com/"&gt;Funq&lt;/a&gt; for .NET platform.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-8366245702858584693?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/8366245702858584693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=8366245702858584693' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8366245702858584693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8366245702858584693'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/08/jsinject.html' title='JsInject -'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-2655093124715583915</id><published>2009-08-02T00:16:00.001-07:00</published><updated>2011-09-10T17:07:40.706-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Tests'/><category scheme='http://www.blogger.com/atom/ns#' term='JsTestDriver'/><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio'/><title type='text'>How to run JsTestDriver with Visual Studio</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;a href="http://code.google.com/p/js-test-driver/"&gt;JsTestDriver&lt;/a&gt; is a powerful framework for running unit tests for JavaScript code. The main advantage of it is that it can executed from command line on automated continuous integration environment, which is vital property of unit testing workflow.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;On the other hand it’s important to have convenient and fast way to run tests while developing. I usually use Visual Studio and JsTestDriver can be attached to it using "&lt;a href="http://blogs.msdn.com/saraford/archive/2008/04/24/did-you-know-you-can-run-external-tools-within-visual-studio-201.aspx"&gt;External Tools&lt;/a&gt;" mechanism.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Prerequisites&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;1. Visual Studio 2010&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;2. Java Runtime&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;3. JsTestDriver (&lt;a href="http://code.google.com/p/js-test-driver/downloads/list"&gt;http://code.google.com/p/js-test-driver/downloads/list&lt;/a&gt;). Just download it and put to local directory. On my machine it z:\Tools\JsTestDriver\JsTestDriver-1.3.2.jar&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Sample ASP.Net solution with JavaScript unit tests&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;For purpose of demonstration lets create small sample project, which contains some JavaScript unit tests.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;1. Open Visual Studio.&lt;br /&gt;2. On the File menu, choose "New Web Site".&lt;br /&gt;3. Create "js" folder for client scripts in the root of web site.&lt;br /&gt;4. Create "production" and "testing" folders within "js" folder. First one - for production scripts and the second one for unit tests, test doubles, test configuration files. Do not forget to configure build scripts to delete "testing" folder and "jsTestDriver.conf" from final release - it is not acceptable to have testing code on production.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;5. Put production scripts in "production" folder. For example - "Greeter.js" with the following contents: &lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;pre&gt;myapp &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;span style="color: #4444ff;"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;myapp.Greeter &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; function () &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt; &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;myapp.Greeter.&lt;span style="color: #2040a0;"&gt;&lt;strong&gt;prototype&lt;/strong&gt;&lt;/span&gt;.greet &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; function (&lt;span style="color: #2040a0;"&gt;&lt;strong&gt;name&lt;/strong&gt;&lt;/span&gt;) &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt; &lt;br /&gt;   &lt;strong&gt;return&lt;/strong&gt; &lt;span style="color: green;"&gt;"Hello "&lt;/span&gt; + &lt;span style="color: #2040a0;"&gt;&lt;strong&gt;name&lt;/strong&gt;&lt;/span&gt; + &lt;span style="color: green;"&gt;"!"&lt;/span&gt;&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;6. Put tests into "testing/tests" folder. For example - "GreeterTest.js" with the following contents:&lt;/div&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;pre&gt;GreeterTest &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; TestCase(&lt;span style="color: green;"&gt;"GreeterTest"&lt;/span&gt;)&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;GreeterTest.&lt;span style="color: #2040a0;"&gt;&lt;strong&gt;prototype&lt;/strong&gt;&lt;/span&gt;.testGreet &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; function () &lt;span style="color: #4444ff;"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt; &lt;br /&gt;    &lt;strong&gt;var&lt;/strong&gt; greeter &lt;span style="color: #4444ff;"&gt;=&lt;/span&gt; &lt;strong&gt;new&lt;/strong&gt; myapp.Greeter()&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;br /&gt;    assertEquals(&lt;span style="color: green;"&gt;"Hello World!"&lt;/span&gt;, greeter.greet(&lt;span style="color: green;"&gt;"World"&lt;/span&gt;))&lt;span style="color: #4444ff;"&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: #4444ff;"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt; &lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;7. Put configuration file “jsTestDriver.conf” into “testing” folder with following contents:&lt;/div&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;span style="font-family: Consolas; font-size: x-small;"&gt;&lt;span style="font-family: Consolas; font-size: x-small;"&gt;server: http://localhost:9876&lt;br /&gt;load:&lt;br /&gt;- production/*.js&lt;br /&gt;- testing/*.js&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Where “http://localhost:9876” is the address of JsTestDriver server, which will be configured later. Make sure that this file is encoded in ASCII format because it happens that JsTestDriver doesn’t recognize UTF8.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;After all operations you will have ASP.Net solution with structure like this:&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_aKmEmrJWYOY/SnVSsHwmKOI/AAAAAAAAEB0/4gYLzfjSVg0/s1600-h/SolutionStructure.JPG"&gt;&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;a href="http://2.bp.blogspot.com/_aKmEmrJWYOY/SnVSsHwmKOI/AAAAAAAAEB0/4gYLzfjSVg0/s1600-h/SolutionStructure.JPG"&gt;&lt;img alt="image" border="0" height="344" src="http://lh4.ggpht.com/-1DVidnCt9m0/Tmv4p2vFtQI/AAAAAAAAUWk/rOvrsrJEdAQ/image%25255B11%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="236" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Configure JsTestDriver server external application&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Open Visual Studio &lt;/li&gt;&lt;li&gt;On the Tools menu, choose External Tools. &lt;/li&gt;&lt;li&gt;In the External Tools dialog box, choose Add, and enter a name "JsTestDriver server Run" in the Title box. &lt;/li&gt;&lt;li&gt;In the Command box, enter the path to the Java Runtime. For example "C:\Program Files\Java\jre6\bin\java.exe" &lt;/li&gt;&lt;li&gt;In the Arguments box, enter arguments in the following format - "-jar {Enter Path to Jar here}JsTestDriver-1.0b.jar --port 9876 --browser {Path to first browser here},{Path to second browser here}". Example from my local machine: -jar z:\Tools\JsTestDriver\JsTestDriver-1.3.2.jar --port 9876 --browser "C:\Program Files\Mozilla Firefox\firefox.exe"&lt;/li&gt;&lt;li&gt;In the Initial directory, enter "$(SolutionDir)" &lt;/li&gt;&lt;li&gt;Select Use output window and then choose OK.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://lh6.ggpht.com/-fh-8ByWJ_uA/Tmv4qrugW9I/AAAAAAAAUWo/XPLLBi8lENw/s1600-h/image%25255B24%25255D.png"&gt;&lt;img alt="image" border="0" height="410" src="http://lh3.ggpht.com/-1FUG3G2JZ_8/Tmv4r7xwMWI/AAAAAAAAUWs/uh5gD2e4esA/image_thumb%25255B6%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="401" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Configure JsTestDriver tests runner external application&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;b&gt;&lt;/b&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Open Visual Studio &lt;/li&gt;&lt;li&gt;On the Tools menu, choose External Tools. &lt;/li&gt;&lt;li&gt;In the External Tools dialog box, choose Add, and enter a name "JsTestDriver Run" in the Title box. &lt;/li&gt;&lt;li&gt;In the Command box, enter the path to the Java Runtime. For example "C:\Program Files\Java\jre6\bin\java.exe" &lt;/li&gt;&lt;li&gt;In the Arguments box, enter arguments in the following format - "-jar {Enter Path to Jar here}JsTestDriver-1.0b.jar --tests all". For example from my machine: -jar z:\Tools\JsTestDriver\JsTestDriver-1.3.2.jar --tests all&lt;/li&gt;&lt;li&gt;In the Initial directory, enter "$(ProjectDir)\js" For example - "$(ProjectDir)\js" &lt;/li&gt;&lt;li&gt;Select Use output window and then choose OK.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://lh5.ggpht.com/-34kASHyk__M/Tmv4soKf3YI/AAAAAAAAUWw/EXXZ_VmAnmI/s1600-h/image%25255B33%25255D.png"&gt;&lt;img alt="image" border="0" height="431" src="http://lh4.ggpht.com/-7nl_HEUfGd8/Tmv4teFP-VI/AAAAAAAAUW0/tCZOsGU30G0/image_thumb%25255B11%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="433" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&amp;nbsp;That's it! Now just assign keyboard shortcut to run "JsTestDriver Run" (In example below it assigned to"Alt + /"):&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/_aKmEmrJWYOY/SnVbEjEHG1I/AAAAAAAAECE/cuTP9D2WGPw/s1600-h/JsTestDriverVisualStudioShortcut.JPG"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5365294664771902290" src="http://1.bp.blogspot.com/_aKmEmrJWYOY/SnVbEjEHG1I/AAAAAAAAECE/cuTP9D2WGPw/s320/JsTestDriverVisualStudioShortcut.JPG" style="cursor: pointer; display: block; height: 190px; margin: 0px auto 10px; text-align: center; width: 320px;" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;strong&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Running Tests&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;First of all we need a server with captured browsers. It can be started/stopped through "Tools" menu:&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_aKmEmrJWYOY/SnVLHX-jV8I/AAAAAAAAEBk/2UIjzRA9ePE/s1600-h/VisualStudioServerSettings.JPG"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5365277121149360066" src="http://4.bp.blogspot.com/_aKmEmrJWYOY/SnVLHX-jV8I/AAAAAAAAEBk/2UIjzRA9ePE/s320/VisualStudioServerSettings.JPG" style="cursor: pointer; display: block; height: 320px; margin: 0px auto 10px; text-align: center; width: 208px;" /&gt;&lt;/a&gt;&lt;br /&gt;Next messages will appear in Output window and browser will appear on screen.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_aKmEmrJWYOY/SnVMmsWIm1I/AAAAAAAAEBs/ZJAgbd_XhsE/s1600-h/VisualStudioServerSettings.JPG"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5365278758704552786" src="http://2.bp.blogspot.com/_aKmEmrJWYOY/SnVMmsWIm1I/AAAAAAAAEBs/ZJAgbd_XhsE/s320/VisualStudioServerSettings.JPG" style="cursor: pointer; display: block; height: 173px; margin: 0px auto 10px; text-align: center; width: 320px;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;To run tests press "Alt + /" or corresponding item in “Tools” menu:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://lh3.ggpht.com/-5Ruczifzw14/Tmv4uZL2TxI/AAAAAAAAUW4/f69RfG_dwG0/s1600-h/image%25255B29%25255D.png"&gt;&lt;img alt="image" border="0" height="407" src="http://lh3.ggpht.com/-1cOY4rmIeJg/Tmv4vGBnRAI/AAAAAAAAUW8/UQLfbe3OMA4/image_thumb%25255B9%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="231" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Results will appear in Output window:&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5365294755995102498" src="http://1.bp.blogspot.com/_aKmEmrJWYOY/SnVbJ25arSI/AAAAAAAAECM/47cjyfMd8ek/s320/JsTestDriverVisualStudioOutput.JPG" style="cursor: pointer; display: block; height: 70px; margin: 0px auto 10px; text-align: center; width: 320px;" /&gt;&lt;/div&gt;There is no need to start new server before each test run – it is possible to reuse already started server instance.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;You can use my &lt;a href="http://code.google.com/p/jsinject/"&gt;JsInject&lt;/a&gt; project as example. It has full suite of tests written in Visual Studio with JsTestDriver.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f08%2fhow-to-run-jstestdriver-with-visual_02.html"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fslmoloch.blogspot.com%2f2009%2f08%2fhow-to-run-jstestdriver-with-visual_02.html" /&gt;&lt;/a&gt;  &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-2655093124715583915?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/2655093124715583915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=2655093124715583915' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2655093124715583915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/2655093124715583915'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/08/how-to-run-jstestdriver-with-visual_02.html' title='How to run JsTestDriver with Visual Studio'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-1DVidnCt9m0/Tmv4p2vFtQI/AAAAAAAAUWk/rOvrsrJEdAQ/s72-c/image%25255B11%25255D.png?imgmax=800' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-5799549736204583403</id><published>2009-08-01T23:51:00.000-07:00</published><updated>2009-08-02T00:17:15.575-07:00</updated><title type='text'>JavaScript dev tools</title><content type='html'>Few months ago i started to work on ASP.NET project with very advanced UI part. Of course there were a lot logic written on Javascript : AJAX, UI templates, Animation and other sweety things. Day by Day count of JS code lines grows and grows, and this was very painfull when someone makes error in JSON syntax for example. Whole solution breaks at one...&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We needed tools to discover bugs in JavaScript at early stages. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First of all we found great tool - JsLint for VS &lt;a href="http://jslint.codeplex.com/"&gt;http://jslint.codeplex.com/&lt;/a&gt;. Such a great thing! It can check JS syntax like compiler for c# does - at a compile time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Secondly we found test runner for Javascript: JsTestDriver - &lt;a href="http://code.google.com/p/js-test-driver/"&gt;http://code.google.com/p/js-test-driver/&lt;/a&gt;. This runner can be utilized under command line and execute all tests under set of browsers. It can be easily integrated in Visual Studio and into continuous integration build scripts. I wish to thank JsTestDriver dev team - great work guys!!!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-5799549736204583403?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/5799549736204583403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=5799549736204583403' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5799549736204583403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/5799549736204583403'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/08/how-to-run-jstestdriver-with-visual.html' title='JavaScript dev tools'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-6704723824150674356</id><published>2009-02-06T03:50:00.000-08:00</published><updated>2009-02-06T03:54:41.346-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='UtcSetLastModified HttpCachePolicy timezone IIS'/><title type='text'>Problem of deployment on server in defferent from build server timezone.</title><content type='html'>&lt;p class="MsoNormal"&gt;&lt;span class="apple-style-span"&gt;&lt;span style=" Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;;font-family:&amp;quot;;font-size:10.0pt;color:#30332D;"&gt;We have a problem with deploying our applications with the 3d pary components and we have to wait until the time zone that we have deployed to has caught up with our time. Here is the error from event log.&lt;span class="Apple-style-span"   style="color: rgb(0, 0, 0);   font-family:Georgia;font-size:16px;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event code: 3005 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event message: An unhandled exception has occurred. &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event time: 2/5/2009 11:42:03 AM &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event time (UTC): 2/5/2009 4:42:03 PM &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event ID: 970fd57dcb5f437798becc86cd174f63 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event sequence: 29 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event occurrence: 1 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Event detail code: 0 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Application information: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Application domain: /LM/W3SVC/1/Root/vrportal-1-128783256796462458 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Trust level: Full &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Application Virtual Path: /vrportal &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Application Path: E:\Projects\VRPortal\ &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Machine name: VANTAGE-10 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Process information: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Process ID: 2252 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Process name: w3wp.exe &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Account name: NT AUTHORITY\NETWORK SERVICE &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Exception information: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Exception type: ArgumentOutOfRangeException &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Exception message: Specified argument was out of the range of valid values.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Parameter name: utcDate &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Request information: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Request URL: http://localhost/vrportal/ScriptResource.axd?d=mr0xEXwm4fB6qSpBbCAzEQtT2tQdIJlxmtQ7qsTv4PAC5lPm9sh5R-nSwNQXSYHRKBB5ZKiZkDdBaFTUH1RKu_tjXHdPXfOj0qU7ZOEKXPHosiT2h4zjxkSO5-poWk760&amp;amp;t=633694545000000000 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Request path: /vrportal/ScriptResource.axd &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;User host address: 127.0.0.1 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;User:&lt;span style="mso-spacerun:yes"&gt;  &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Is authenticated: False &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Authentication Type:&lt;span style="mso-spacerun:yes"&gt;  &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Thread account name: NT AUTHORITY\NETWORK SERVICE &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Thread information: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Thread ID: 1 &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Thread account name: NT AUTHORITY\NETWORK SERVICE &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Is impersonating: False &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;Stack trace:&lt;span style="mso-spacerun:yes"&gt;    &lt;/span&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;at System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate)&lt;o:p&gt;&lt;/o:p&gt;&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.HttpCachePolicy.SetLastModified(DateTime date)&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.Handlers.ScriptResourceHandler.PrepareResponseCache(HttpResponse response, Assembly assembly)&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.Handlers.ScriptResourceHandler.ProcessRequest(HttpContext context)&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.Handlers.ScriptResourceHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext context)&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt;   &lt;/span&gt;at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp;amp; completedSynchronously)&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;Custom event details: &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;i style="mso-bidi-font-style:normal"&gt;&lt;span style="font-size:8.0pt;"&gt;For more information, see Help and Support Center at &lt;a href="http://go.microsoft.com/fwlink/events.asp"&gt;http://go.microsoft.com/fwlink/events.asp&lt;/a&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;Due the difference in timezones between our build server and deployment server in customer network the build has file modification date that lies in future for customer timezone.&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;File modification time is sent to client as Last-Modified HTTP header and file is cached in user cache with this time. Then requesting file at second time - browser &lt;span style="mso-spacerun:yes"&gt; &lt;/span&gt;use date from cache and set If-Modified-Since: conditional get header in HTTP request expecting to receive 302 Not Modified response or content of new file. &lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;Here is a problem. Once user will receive file with incorrect date (in future) - If-Modified-Since requests will be unable to detect if file was changed and use stale data. &lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;This is a reason why Microsoft throws an exception in this situation. This is absolutely legit reason for this.&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;Here is the link on microsoft site: &lt;a href="http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=103158"&gt;http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=103158&lt;/a&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-6704723824150674356?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/6704723824150674356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=6704723824150674356' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6704723824150674356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/6704723824150674356'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/02/we-have-problem-with-deploying-our.html' title='Problem of deployment on server in defferent from build server timezone.'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1377949485034122577</id><published>2009-02-05T07:05:00.000-08:00</published><updated>2009-02-05T07:06:31.786-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.Net ResolveUrl'/><title type='text'>How to resolve relative url's without ResolveUrl</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: Arial; font-size: 13px; "&gt;&lt;p style="margin-bottom: 10px; margin-top: 10px; "&gt;Sometimes you need to resolve relative url's without ResolveUrl. If the code is executing outside a Control, for example in an IHttpHandler or business layer code somewhere that has no reference to a Control, you can't call Control.ResolveUrl.&lt;/p&gt;&lt;p style="margin-bottom: 10px; margin-top: 10px; "&gt;The &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.virtualpathutility.aspx" style="color: rgb(17, 68, 183); "&gt;System.Web.VirtualPathUtility&lt;/a&gt; class has some very useful method for converting from an app relative path to an absolute path:&lt;/p&gt;&lt;div style="font-family: Consolas; font-size: 10pt; color: black; background-image: initial; background-repeat: initial; background-attachment: initial; -webkit-background-clip: initial; -webkit-background-origin: initial; background-color: white; background-position: initial initial; "&gt;&lt;p style="margin-right: 0px; margin-left: 0px; margin-bottom: 10px; margin-top: 10px; "&gt;    &lt;span style="color: blue; "&gt;string&lt;/span&gt; absoluteUrl = &lt;span style="color: rgb(43, 145, 175); "&gt;VirtualPathUtility&lt;/span&gt;.ToAbsolute(relativeUrl);&lt;/p&gt;&lt;p style="margin-right: 0px; margin-left: 0px; margin-bottom: 10px; margin-top: 10px; "&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-right: 0px; margin-left: 0px; margin-bottom: 10px; margin-top: 10px; "&gt;From &lt;a href="http://dotnettipoftheday.org/tips/VirtualPathUtility-ToAbsolute.aspx"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1377949485034122577?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1377949485034122577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1377949485034122577' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1377949485034122577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1377949485034122577'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/02/how-to-resolve-relative-urls-without.html' title='How to resolve relative url&apos;s without ResolveUrl'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-931111104959663884</id><published>2009-01-31T08:47:00.000-08:00</published><updated>2009-02-05T07:05:13.993-08:00</updated><title type='text'></title><content type='html'>&lt;div style="MARGIN: 0px auto 10px; TEXT-ALIGN: center"&gt;Me and my dog Grom during walking in the park.&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_aKmEmrJWYOY/SYSAqd6UOlI/AAAAAAAAD7I/DNFWp2ap9D4/s1600-h/2008.01.31+063.jpg"&gt;&lt;img alt="" src="http://4.bp.blogspot.com/_aKmEmrJWYOY/SYSAqd6UOlI/AAAAAAAAD7I/DNFWp2ap9D4/s320/2008.01.31+063.jpg" border="0" /&gt;&lt;/a&gt; &lt;/div&gt;&lt;div style="clear:both; text-align:CENTER"&gt;&lt;a href="http://picasa.google.com/blogger/" target="ext"&gt;&lt;img src="http://photos1.blogger.com/pbp.gif" alt="Posted by Picasa" style="border: 0px none ; padding: 0px; background: transparent none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial;" align="middle" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-931111104959663884?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/931111104959663884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=931111104959663884' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/931111104959663884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/931111104959663884'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/01/blog-post.html' title=''/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_aKmEmrJWYOY/SYSAqd6UOlI/AAAAAAAAD7I/DNFWp2ap9D4/s72-c/2008.01.31+063.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-1881877176771374673</id><published>2009-01-07T14:20:00.000-08:00</published><updated>2009-01-07T14:21:18.626-08:00</updated><title type='text'>TutorialCachingStory</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: arial; font-size: 13px; "&gt;&lt;div id="wikiheader" style="margin-bottom: 1em; "&gt;&lt;div style="font-style: italic; margin-top: 3px; "&gt;This is a story of Caching&lt;/div&gt;&lt;/div&gt;&lt;p style="max-width: 65em; "&gt;&lt;i&gt;ed note: this is an overview of basic memcached use case, and how memcached clients work&lt;/i&gt;&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Two plucky adventurers, Programmer and Sysadmin, set out on a journey. Together they make websites. Websites with webservers and databases. Users from all over the Internet talk to the webservers and ask them to make pages for them. The webservers ask the databases for junk they need to make the pages. Programmer codes, Sysadmin adds webservers and database servers.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;One day the Sysadmin realizes that their database is sick! It's spewing bile and red stuff all over! Sysadmin declares it has a fever, a load average of 20! Programmer asks Sysadmin, "well, what can we do?" Sysadmin says, "I heard about this great thing called memcached. It really helped livejournal!" "Okay, let's try it!" says the Programmer.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Our plucky Sysadmin eyes his webservers, of which he has six. He decides to use three of them to run the 'memcached' server. Sysadmin adds a gigabyte of ram to each webserver, and starts up memcached with a limit of 1 gigabyte each. So he has three memcached instances, each can hold up to 1 gigabyte of data. So the Programmer and the Sysadmin step back and behold their glorious memcached!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"So now what?" they say, "it's not DOING anything!" The memcacheds aren't talking to anything and they certainly don't have any data. And NOW their database has a load of 25!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Our adventurous Programmer grabs the pecl/memcache client library manual, which the plucky Sysadmin has helpfully installed on all SIX webservers. "Never fear!" he says. "I've got an idea!" He takes the IP addresses and port numbers of the THREE memcacheds and adds them to an array in php.&lt;/p&gt;&lt;pre class="prettyprint" style="font-size: 110%; margin-left: 2em; padding-top: 0.5em; padding-right: 0.5em; padding-bottom: 0.5em; padding-left: 0.5em; border-left-width: 3px; border-left-style: solid; border-left-color: rgb(204, 204, 204); "&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$MEMCACHE_SERVERS &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; array&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"10.1.1.1"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="com" style="color: rgb(136, 0, 0); "&gt;//web1&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"10.1.1.2"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="com" style="color: rgb(136, 0, 0); "&gt;//web2&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"10.1.1.3"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="com" style="color: rgb(136, 0, 0); "&gt;//web3&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;);&lt;/span&gt;&lt;/pre&gt;&lt;p style="max-width: 65em; "&gt;Then he makes an object, which he cleverly calls '$memcache'.&lt;/p&gt;&lt;pre class="prettyprint" style="font-size: 110%; margin-left: 2em; padding-top: 0.5em; padding-right: 0.5em; padding-bottom: 0.5em; padding-left: 0.5em; border-left-width: 3px; border-left-style: solid; border-left-color: rgb(204, 204, 204); "&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$memcache &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;new&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="typ" style="color: rgb(102, 0, 102); "&gt;Memcache&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;();&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;foreach&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$MEMCACHE_SERVERS &lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;as&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $server&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;){&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    $memcache&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;-&gt;&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;addServer &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $server &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;);&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;p style="max-width: 65em; "&gt;Now Programmer thinks. He thinks and thinks and thinks. "I know!" he says. "There's this thing on the front page that runs &lt;tt&gt;SELECT * FROM hugetable WHERE timestamp &gt; lastweek ORDER BY timestamp ASC LIMIT 50000;&lt;/tt&gt; and it takes five seconds!" "Let's put it in memcached," he says. So he wraps his code for the SELECT and uses his $memcache object. His code asks:&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Are the results of this select in memcache? If not, run the query, take the results, and PUT it in memcache! Like so:&lt;/p&gt;&lt;pre class="prettyprint" style="font-size: 110%; margin-left: 2em; padding-top: 0.5em; padding-right: 0.5em; padding-bottom: 0.5em; padding-left: 0.5em; border-left-width: 3px; border-left-style: solid; border-left-color: rgb(204, 204, 204); "&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$huge_data_for_frong_page &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $memcache&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;-&gt;&lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;get&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"huge_data_for_frong_page"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;);&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;if&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$huge_data_for_frong_page &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;===&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;false&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;){&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    $huge_data_for_frong_page &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; array&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;();&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    $sql &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"SELECT * FROM hugetable WHERE timestamp &gt; lastweek ORDER BY timestamp ASC LIMIT 50000"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;;&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    $res &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; mysql_query&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$sql&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $mysql_connection&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;);&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;while&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$rec &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; mysql_fetch_assoc&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;$res&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;)){&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;        $huge_data_for_frong_page&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;[]&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;=&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $rec&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;;&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;}&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class="com" style="color: rgb(136, 0, 0); "&gt;// cache for 5 minutes&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;    $memcache&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;-&gt;&lt;/span&gt;&lt;span class="kwd" style="color: rgb(0, 0, 136); "&gt;set&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;(&lt;/span&gt;&lt;span class="str" style="color: rgb(0, 136, 0); "&gt;"huge_data_for_frong_page"&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; $huge_data_for_frong_page&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;,&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt; &lt;/span&gt;&lt;span class="lit" style="color: rgb(0, 102, 102); "&gt;600&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;);&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="pun" style="color: rgb(102, 102, 0); "&gt;}&lt;/span&gt;&lt;span class="pln" style="color: rgb(0, 0, 0); "&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="com" style="color: rgb(136, 0, 0); "&gt;// use $huge_data_for_frong_page how you please&lt;/span&gt;&lt;/pre&gt;&lt;p style="max-width: 65em; "&gt;Programmer pushes code. Sysadmin sweats. BAM! DB load is down to 10! The website is pretty fast now. So now, the Sysadmin puzzles, "What the HELL just happened!?" "I put graphs on my memcacheds! I used cacti, and this is what I see! I see traffic to one memcached, but I made three :(." So, the Sysadmin quickly learns the ascii protocol and telnets to port 11211 on each memcached and asks it:&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Hey, 'get huge_data_for_front_page' are you there?&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;The first memcached does not answer...&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;The second memcached does not answer...&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;The third memcached, however, spits back a huge glob of crap into his telnet session! There's the data! Only once memcached has the key that the Programmer cached!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Puzzled, he asks on the mailing list. They all respond in unison, "It's a distributed cache! That's what it does!" But what does that mean? Still confused, and a little scared for his life, the Sysadmin asks the Programmer to cache a few more things. "Let's see what happens. We're curious folk. We can figure this one out," says the Sysadmin.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"Well, there is another query that is not slow, but is run 100 times per second. Maybe that would help," says the Programmer. So he wraps that up like he did before. Sure enough, the server loads drops to 8!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;So the Programmer codes more and more things get cached. He uses new techniques. "I found them on the list and the faq! What nice blokes," he says. The DB load drops; 7, 5, 3, 2, 1!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"Okay," says the Sysadmin, "let's try again." Now he looks at the graphs. ALL of the memcacheds are running! All of them are getting requests! This is great! They're all used!&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;So again, he takes keys that the Programmer uses and looks for them on his memcached servers. 'get this_key' 'get that_key' But each time he does this, he only finds each key on one memcached! Now WHY would you do this, he thinks? And he puzzles all night. That's silly! Don't you want the keys to be on all memcacheds?&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"But wait", he thinks "I gave each memcached 1 gigabyte of memory, and that means, in total, I can cache three gigabytes of my database, instead of just ONE! Oh man, this is great," he thinks. "This'll save me a ton of cash. Brad Fitzpatrick, I love your ass!"&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"But hmm, the next problem, and this one's a puzzler, this webserver right here, this one runing memcached it's old, it's sick and needs to be upgraded. But in order to do that I have to take it offline! What will happen to my poor memcache cluster? Eh, let's find out," he says, and he shuts down the box. Now he looks at his graphs. "Oh noes, the DB load, it's gone up in stride! The load isn't one, it's now two. Hmm, but still tolerable. All of the other memcacheds are still getting traffic. This ain't so bad. Just a few cache misses, and I'm almost done with my work. So he turns the machine back on, and puts memcached back to work. After a few minutes, the DB load drops again back down to 1, where it should always be.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;"The cache restored itself! I get it now. If it's not available it just means a few of my requests get missed. But it's not enough to kill me. That's pretty sweet."&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;So, the Programmer and Sysadmin continue to build websites. They continue to cache. When they have questions, they ask the mailing list or read the faq again. They watch their graphs. And all live happily ever after.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Author: Dormando via IRC. Edited by Brian Moon for fun. Further fun editing by Emufarmers.&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="max-width: 65em; "&gt;Got from here: &lt;a href="http://code.google.com/p/memcached/wiki/TutorialCachingStory"&gt;http://code.google.com/p/memcached/wiki/TutorialCachingStory&lt;/a&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-1881877176771374673?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/1881877176771374673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=1881877176771374673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1881877176771374673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/1881877176771374673'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2009/01/tutorialcachingstory.html' title='TutorialCachingStory'/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3321529878072237426.post-8968746584226572305</id><published>2008-11-26T07:11:00.001-08:00</published><updated>2008-11-26T07:11:52.881-08:00</updated><title type='text'></title><content type='html'>I've created my first blog! wow...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3321529878072237426-8968746584226572305?l=slmoloch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slmoloch.blogspot.com/feeds/8968746584226572305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3321529878072237426&amp;postID=8968746584226572305' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8968746584226572305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3321529878072237426/posts/default/8968746584226572305'/><link rel='alternate' type='text/html' href='http://slmoloch.blogspot.com/2008/11/ive-created-my-first-blog-wow.html' title=''/><author><name>Yauheni Sivukha</name><uri>http://www.blogger.com/profile/15188148633003979921</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://3.bp.blogspot.com/_aKmEmrJWYOY/S7EUnkfdc2I/AAAAAAAANZk/IKmC7oXq0gM/S220/yauheni_sivukha.jpg'/></author><thr:total>0</thr:total></entry></feed>
