| |
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. |
|