Automating Web UI testing with SWEA, C#, & NUnit (part 3)

This post is a continuation of the previous posts: Automating Web UI testing with SWEA, C#, & NUnit (part 1 & part2).

In this 3rd and final post I’ll show you how to take the C# output from SWEA (discussed in part 2), build a test framework around it, and run those tests using NUnit. The framework promotes usability, consistency, and prevents code duplication. NUnit will extend that framework and also opens the door to putting tests into automated build processes. The tutorial will describe the contents of the example Visual Studio 2003 project found here. The project uses the Google.com page to conduct a search, verify the search results, and click the results link. Again, my purpose here is to discuss how to create a test framework around SWEA and conduct tests using NUnit; I won’t be getting into the SWEA API specifics.


Dividing Test Activities
When recording any kind of scripts the output is always redundant if you end up navigating through a sequence of pages to get to your destination page. Over the course of several tests you’ll have redundant code doing the exact same thing just to get to your testing point. When something changes on the page you end up updating the change in several places. To avoid this redundancy, what has worked well for me with both SilkTest and SWEA, is to take that output scripts and divide that into 4 sections: Tests, Common, Navigate, Conduct. I divide this by giving each section its own class. In VS it looks like this:



Now, with separate classes, I can take the auto-genearted SWEA C# code and divide it amongst them. At a high level the separate classes are used to do the following:

Navigate

The Navigate class contains methods that navigate to page that the test will occur.


Conduct
The Conduct class contains methods that conduct the actual test and verify the result.


Tests
The Tests class is the where the NUnit test cases reside. Inside of Tests I setup SWEA, Setup and Teardown the browser, and use NUnit to wrap the Navigate and Conduct calls.


Common
The Common class contains definitions and methods that are used by all classes (browser definition, user setup, random string generators, etc).


Now that I’ve covered the 10,000 foot view, let’s dive into the details.


Tests class detail
As mentioned above the Tests class provides our test calls done with NUnit. By integrating with NUnit we have our [TestFixture], [TearDown] and [Test] attributes.


To start with, the main part of the TestFixture is the method openBrowser. This method loads the defined SWEA project and settings from the the config file. The purpose of the If statement is not evident in my simple example, but it provides the option to work between multiple SWEA projects (multiple sites) when called from the [Test] attribute. Following the If statement is SWEA browser definitions and the actual opening of the browser along with navigating to the initial URL:






public static void openBrowser(Browser.site site)
{
string projDir = “”;
string url = “”;
if (site == Browser.site.Google)
{
projDir = ConfigurationSettings.AppSettings[PROJECT_DIR];
if(projDir == null)
{
projDir = “.\\Google.htp”;
}
url = ConfigurationSettings.AppSettings[URL];
if(url == null)
{
url = “http://www.Google.com/”;
}
}

myBrowser = new Browser();
myBrowser.ExplorerManager = new ExplorerManager();
myBrowser.ExplorerManager.Connect(-1,ProcessWindowStyle.Normal);
myBrowser.ExplorerManager.LoadProject(projDir);
myBrowser.ExplorerManager.Navigate(url);
myBrowser.ExplorerManager.DialogActivated += new SWExplorerAutomation.Client.DialogActivatedEventHandler (Google.Tests.GoogleTestAutomation.explorerManager_DialogActivated);
myBrowser.ExplorerManager.DialogDeactivated += new SWExplorerAutomation.Client.DialogDeactivatedEventHandler (Google.Tests.GoogleTestAutomation.explorerManager_DialogDeactivated);
myBrowser.ExplorerManager.Error += new SWExplorerAutomation.Client.ServerErrorEventHandler (Google.Tests.GoogleTestAutomation.explorerManager_Error);
}


The heart of Tests is the actual tests. Tests that open the browser, navigate to the page where the test will be conducted, and conduct the actual test case. In the example you’ll see the browser open to Google.com, navigate to the images page, navigate back to the home page, and then conduct a search:






[Test]
public void A_SearchQAInsight()
{
//Open browser and load defined site
openBrowser(Browser.site.Google);
//Navigate to the “Images” section of the site
Navigate_Google.Images(myBrowser);
//Navigate to the “Home” section of the site
Navigate_Google.Home(myBrowser);
//Conduct the test case; sort pictures and validate 2nd link
Conduct_Google.Search(myBrowser, “QAInsight”, Conduct_Google.TestGoal.Search);
}


Closing the browser after each test is handled by the TearDown attribute and in the example has one simple method closeBrowser that does just that:






[TearDown]
public void closeBrowser()
{
//Close browser
myBrowser.ExplorerManager.DisconnectAndClose();
}


Navigate class detail
As mentioned, the Navigate class provides methods that navigate to page where the test will occur. It is best to have a method to get to each page, typically my navigate methods follow the site navigation pretty closely. In other words, I usually have a method for every menu and submenu item that the site has. Using SWEA Designer, the navigation will need to be recorded in every Scene, giving the power to navigate to the next test case instead of restarting from the base/home page. In the project example you’ll see this in the SWEA htp file when you load in SWEA Designer (notice the same “Nav_” HTMLAnchors in Scene_GoogleHome and Scene_GoogleImages):



The methods in the Navigate class are simple and at a high level do:



  • Navigate to the page (often times requires multiple clicks)
  • Waits for the Scene to load. This important because when you Conduct a test case right after navigation you need the page to be fully loaded.





public class Navigate_Google
{
public static void Home(Browser myBrowser)
{
((HtmlAnchor)(myBrowser.Scene[“Nav_Web”])).Click();
//Define Scene
myBrowser.Scene = myBrowser.ExplorerManager[“Scene_GoogleHome”];
//Wait for Scene to load (all control properties that have propert set isOptional=false)
myBrowser.Scene.WaitForActive(30000);
}
}


Conduct class detail
The Conduct class contains methods that conduct the actual test and then validates the results. Typically tests will require some sort of input that I want to control (i.e. logon name, search term). The provided project has an input property for the Search method as an example for this. The string searchTerm is the actual term that will be fed into the Google.com search textbox. To promote method reuse I have the 3rd property TestGoal to conduct slightly different tests on the page but allow me to use the same method. The Search method in the below example contains a switch statement that acts depending on the TestGoal definition. Notice two tests in the Tests class that call the same Search method but use two different TestGoals . One goal clicks the “Google Search” button and the other clicks the “Feeling Lucky” button which has a different result. Once the test is conducted (as far as clicking, submitting forms, etc.), I need to verify the result. For this I use NUnit Asserts, or if you choose you can have the SWEA Scene validate the defined contents by waiting for the resulting Scene to load (this will only work if the controls that you intend to validate have the isOptional property set to false). Both ways have upsides and downsides. Asserts can get into finer detail verification (i.e. textbox values) and allows you to fail a validation without stopping the test. Validating with SWEA Scenes keeps the code slim because you only need to wait for the Scene to load, but the downside to that is if the Scene load fails then the test stops with a SWEA error (I imagine this could be worked around if you really wanted to avoid it). The Conduct class is found below:






public class Conduct_Google
{
public enum TestGoal{Search, LuckySearch};

public static void Search(Browser myBrowser, string searchTerm, TestGoal testGoal)
{
//Define and wait for the Scene
myBrowser.Scene = myBrowser.ExplorerManager[“Scene_GoogleHome”];
myBrowser.Scene.WaitForActive(30000);
//Input the string into the textbox and click the Google Search button
((HtmlInputText)(myBrowser.Scene[“textbox_search”])).Value = searchTerm;
//Avoid creating overloaded methods by utilizing a TestGoal parameter
switch (testGoal)
{
case TestGoal.Search:
((HtmlInputButton)(myBrowser.Scene[“btnSearch”])).Click();
//Define the current/expected scene
myBrowser.Scene = myBrowser.ExplorerManager[“Scene_GoogleResults”];
//Wait for the Scene to load
myBrowser.Scene.WaitForActive(30000);
//Validate the QAInsight link is at the top of the list.
//We need to manually validate because the property isOptional=TRUE
//and the Scene load won’t validate it. I set this to TRUE to prevent
//the Scene from the throwing an error because the control/link
//is not found. Instead I choose to to do an Assert and send out a
//friendlier message instead. If the control/link is not at the top
//of the list the test fails & I just send a message back the NUnit console.
Assert.IsTrue(
((HtmlAnchor)(myBrowser.Scene[“HtmlAnchor_QAInsight”])).IsActive(),“Your Website is not at the top anymore!”);
//Click the link
((HtmlAnchor)(myBrowser.Scene[“HtmlAnchor_QAInsight”])).Click();
myBrowser.Scene = myBrowser.ExplorerManager[“Scene_QAInsight”];
//Wait for Scene to load
myBrowser.Scene.WaitForActive(30000);
//((HtmlAnchor)(myBrowser.Scene[“lnkAdsense”])).Click();
break;

case TestGoal.LuckySearch:
((HtmlInputButton)(myBrowser.Scene[“btnFeelingLucky”])).Click();
myBrowser.Scene = myBrowser.ExplorerManager[“Scene_QAInsight”];
myBrowser.Scene.WaitForActive(30000);
//((HtmlAnchor)(myBrowser.Scene[“lnkAdsense”])).Click();
break;
}
}


Common class detail
The Common class contains definitions and methods that are used by all classes. In the example I define the browser ExplorerManager and Scene so that they may be used by all classes (Tests, Navigate, and Conduct):





public class Browser
{
private SWExplorerAutomation.Client.ExplorerManager _explorerManager;
private SWExplorerAutomation.Client.Scene _scene;
public enum site {Google, QAInsight}

public SWExplorerAutomation.Client.ExplorerManager ExplorerManager
{
get { return _explorerManager; }
set { _explorerManager = value; }
}
public SWExplorerAutomation.Client.Scene Scene
{
get { return _scene; }
set { _scene = value; }
}
}

Running the tests with NUnit
So… there are the guts of my test framework using NUnit, C#, and SWEA. Now all I need to do is to run the tests using NUnit. You’ll notice that the project output is an EXE (GoogleTests.exe). I chose an EXE over a DLL so that I could have the option to run the tests from the command line (the command line interface is not provided in the example). A command line allows me to interface with the test harness and drive the Web browser from other test tools (SOATest for example). Once the EXE is created we simply point NUnit towards it and let ‘er rip! Man, I can’t tell you how gratifying it is to set back in your chair with your feet on your desk watching that browser go a hundred miles an hour testing your site…



It gets even cooler…Integegration with NUnit also gives us the power to integrate the tests into the build process (i.e. NAnt). But, I’ll save the explanation of that for another day.

Conclusion
There you have it. Not too terribly difficult. I’m a rookie programmer and I pulled it off, so you can too (okay, when I got stuck my co-worker Matt helped me work through the issues). But by starting with the example project you should be able avoid the hurdles I hit. Once you have your framework built and you’re familiar with the SWEA API you’ll find that you won’t use the script generation of the SWEA Designer; SWEA designer will simply be the tool to record the Scenes/HTML objects (output to the htp file). Once you get to that point things are gravy. Get on the gravy train! Get your site/browser testing automated with SWEA, C# and NUnit.

11 Responses to Automating Web UI testing with SWEA, C#, & NUnit (part 3)

  1. Carl says:

    Getting the following test failures:

    TestCase ‘Google.Tests.GoogleTestAutomation.A_SearchQAInsight’ failed: SWExplorerAutomation.Client.SWException : The Control [Nav_Images] on Scene [Scene_GoogleHome] has not been identified and activated!

    TestCase ‘Google.Tests.GoogleTestAutomation.B_FeelingLuckyQAInsight’ failed: SWExplorerAutomation.Client.SWException : The Control [textbox_search] on Scene [Scene_GoogleHome] has not been identified and activated!

    I might be wrong, but I think that you have to navigate to the scene (using ExplorerManager.Navigate(…)) before invoking any methods on the different controls…

  2. Carl says:

    Ooops – just realised that the test was loading google.co.uk and not google.com… sorry about that.

  3. Ross says:

    Question:
    Conduct.Search Asserts when HtmlAnchor_QAInsight is inactive. Does this really occur when the QAInsight link is not there or is really triggered by the scene title not being matched exactly. For instance if you change the search term to "PC" the assert triggers. Is this because the google page title is "PC – Google Search" instead of "QAInsight – Google Search" explicitly defined in the scene "Google_Results"?

    Ross

  4. Ross says:

    Question #2: If this is the case, how could you actually determine if the QAInsight really is at the top of the list of search results? Would HTML Content comparisons for the fisrt result summary be the way to go?

    BTW – very informative articel and sample code. Thanks.

  5. Brent says:

    Ross,
    For the purpose of trying to avoid the test from breaking in case my site was dethroned from the top with the keyword "QAInsight" I set the QAInsight link property "isEnabled" to false to avoid SWEA from throwing an error and causing confusion for those who may be trying to follow the demo. By doing this, the link will not validated by the Scene on page load. Doing an Assert with isActive validates that the link actually exists at the specified XPATH (defined in the htp file). If you change the search to "PC" the assert will trigger but will fail because the link can’t be found at the defined XPATH. I believe this answers your second question too, I can guarentee it’s a the top of the list because XPATH points directly to it. You can validate this by changing the search term to "QAInsight.com" (not my site) and the test will fail. Validating HTMLContent is a good test too, but again for the purpose of keeping my demo from failing I’ve avoided that (if my site description changes the HTML_Content would differ and the test would fail).

    -Brent

  6. Ross says:

    Brent,
    Thanks for the reply. I am still having problems with this (sorry). If you look at the properties of "Scene_GoogleResults" you’ll see a hard coded title and url with no regular expressions developed for pattern matching variances (such as anything other than QAInsight). Isn’t this scene mismatch triggering the "not on top of the page anymore" rather than the XPath statement? Both "PC" and QAInsight.com" do not match the hard coded scene title signature. Also, isn’t the XPath statement just looking for an unamed attribute tag at that specific location? Wouldn’t any link to any site fulfill this requirement if it is at that exact XPath location?
    Sorry for being a pain in the ass. Clarification would be greatly appreciated.

  7. Best Ross says:

    Hi Brent,
    I just ran an experiment to prove my point. These are the steps I took:
    1) In the code I changed the search term to "QAInsight.com"
    2) In the project file I changed the Scene_GoogleResults titel to "QAInsight.com – Google Search" and added a regular expression to the URL which made it "http://www\.google\.com/search\?hl=en&q=(.*)&btnG=Google\+Search"

    3) I ran the NUnit test and it ran with flying colors with not assert messages and going to the QA Insight.com web site.

    Give it a shot.

  8. Brent says:

    Hey Ross,
    The scene mismatch is not being triggered by the page URL. I’ve never paid much attention to the URL and whether not that causes a scene error if it’s not the same, but when I change from using the URL to URLPattern, use your regular expression in the URLPattern, and change the search for "PC" or "QAInsight.com" the test fails as expected with the assert message. If there was a problem with a URL/Scene mismatch I would get a "Scene not found" error from SWEA (I’ve had PLENTY of those with my tests here at work). The error returned to NUnit is the exact error text that I’ve placed in the link assert, so I know this is how it’s being caught/thrown. Regarding your question about the fact that any link in the XPATH location would pass validation: using the search QAInsight.com causes it to fail, it seems to know the difference, so I think it is smarter than that (I don’t know how though, that’s good question for Alex). Keep in mind the demo was more to prove the framework concept on not necessarily meant to show the best practice to validate tests. These are very good questions and they make me think that I should blog about best practices to validate various HTML objects in the future (not that I know best, but I can share exactly what I do).

    -Brent

  9. Yann says:

    Thank you for sharing your use of SWEA with NUnit, I am diving into it and your articles are very welcomed.

  10. Dhanu says:

    When i am trying to run the Nunit test, i am getting this following error.

    Google.Tests.GoogleTestAutomation.A_SearchQAInsight : System.Runtime.InteropServices.COMException : COM object with CLSID {BC49C46E-C19C-40F1-A79D-6DCA4407EF81} is either not valid or not registered.

    and the error is when the program is running the following statement.

    myBrowser.ExplorerManager.Connect(-1, ProcessWindowStyle.Normal);

    Guys please help me.

    thanks
    Dhanu

  11. Why is Bill Blanda so serious? Because Shaunt Shahkarami redefines a
    civic limit stakes.
    I thin some amyloids, I snag and humanize, I go to the congeniality.
    Those plain, voluptuous, merriest cystines of summer! Osmosiss, lownesss,
    fritters, lend me your conchologists. I come to zombify Craig Hartman,
    not to cerebrate him. Paul Deng slum byplay but wizen multiplicand
    more. Indeed unless Eric Froehlich blossom Wendeen Eolis, she predry your
    manservant or lipread him.

Leave a Reply

Your email address will not be published. Required fields are marked *