Monday, January 16, 2012

The simple art of creating a log file for your script

Some of us will have no use for ever creating our own logs, because we use fancy environments like QTP or Test Complete that will generate pretty detailed results for our automated scripts. However, even if this is the case, there are instances where generating a log could be used as a pretty nifty debugging tool, or serve as a pretty cool sanity check for scripts that run long. Here's my basic methodology for creating a log using C# and Selenium Remote Webdriver.

[TestFixture]
public class Tests
{
     //Create a list, you will add items to the list that will become your log
     public static List<string> log = new List<string>();
     [Test]
     public void SomeTest()
     {
           //Capture the browser name and version
           string broswerName = ((RemoteWebDriver)driver).Capabilities.BrowserName);           
           string broswerVersion = ((RemoteWebDriver)driver).Capabilities.BrowserVersion);
           log.Add("Test was run on browser: " + browserName + " : " + browserVersion);
           //Log the time the test execution began
           log.Add("Test began at: " + DateTime.Now);
           //Use a stopwatch to capture times of different events.
           Stopwatch timer = new Stopwatch();
           timer.Start();
           //Do ACTION A
           log.Add("ACTION A took: " + stopwatch.ElapsedMilliseconds + "ms");
           timer.Restart();
           //Do ACTION B
           log.Add("ACTION B took: " + stopwatch.ElapsedMilliseconds + "ms");
           timer.Restart();
           //Log the time the test execution completed
           log.Add("Test ended at: " + DateTime.Now);
          //Create the file to write the log to. I want the file to always be unique, so at the end I take the date/time up to the second and strip out all the character that would cause conflict for a file name 
           string file = "name" + DateTime.Now.ToString().Replace(":", "").Replace("/", "") + ".txt"
           //Open the StreamWriter to write the log file. Create the log file, iterate through our list to populate each line of the log file. .WriteLine will start a new line.
           using (StreamWriter sw = File.CreateText("C:\\" + file ))
            {
                  for (int x = 0; x < log.Count; x++)
                        sw.WriteLine(log[x]);
            }
     } 
}

This should create a log that looks something like this:

Test was run on browser: Firefox : 8.0.1
Test began at: 01/14/2012 7:00PM
ACTION A took: 30000ms
ACTION B took: 30242ms
Test ended at: 01/14/2012 7:01PM


Sunday, December 11, 2011

Procedural vs Object Oriented Script Writing


It's been my experience that a lot of testers that take the automation route don't have a lot of programming experience. I think this is because the natural progression of a QA Engineer would be to one day become a developer. Those of us that have decided to dedicate our careers to automated QA are pretty rare, possessing skill-sets of both QA analyst and developer (in my case, a very bad developer :)) Having a novice set of programming skills, I've noticed that any time I approach a new project my scripts tend to take a procedural path. That is to say, my scripts take a linear path, and are extremely limited as far as code and object re-use goes. It's a particularly challenging task for someone with little programming background to imagine how an automated test script could be created in an object-oriented way. Below is an example of how I turned my procedural script into an object-oriented one: 

Here's a simple login script. Notice in the procedural script example, I'm constantly passing arguments into a method in a HelperMethod class, this class is simply filled with static methods that are executing tasks.

PROCEDURAL SCRIPT EXAMPLE

    [TestFixture]
    public class LoginTests
    {
        [Test]
        public void Login_Test()
        {

                HelperMethods.OpenPage("http://fakeloginpage.com");
                HelperMethods.InputLoginInfo("FakeID", "FakePW");
                HelperMethods.ClickLoginButton();
        }
    }


Here is the code re-written in an Object-Oriented way. Notice that the static methods are gone, and everything is occurring with an object. The immediate benefit of this is that the properties of the object are RE-USABLE later on!!!! Say, for example, if after login the login name was supposed to appear in the upper right hand corner of the next page. You could easily verify it by referencing a property of the object, instead of having to hard-code the login everywhere.

OBJECT ORIENTED EXAMPLE

        public class Login
        {
            public string url = "http://fakeloginpage.com";
            public string id = "FakeID";
            public string password = "FakePW";
            public void Go()
            {
                driver.Navigate().GoToUrl(url);
                driver.FindElement(By.Id("login_id").SendKeys(id);
                driver.FindElement(By.Id("login_pw").SendKeys(password);
                driver.FindElement(By.Id("login_button").Click();
            }
         }

Here is how this would be used in a test script:


    [TestFixture]
    public class MyTests
    {
        [Test]
        public void LoginTest()
        {   
             Login login = new Login();
             //note that your url/id/pw variables are public, so you can easily modify them here
             //it would look like this:
             //login.url = "http://newfakeurl.com";
             login.Go();
        }
     }


Monday, November 21, 2011

How am I handling page loading?

I'm working on an application that does many postbacks to the server. I've been having extreme difficulty handling loading, but I'd like to share my experience with everyone. I'm purposefully not saying what language my app is written in, because I believe the principles I'm about to discuss apply globally.

MY LESSON LEARNED:
As a little background, the application I'm working on is an application that we're currently integrating with. Because of this, my initial thought was to code as many scrips as possible and not worry about the quality. Instead of thinking about a smart way to handle load times, I relied heavily (HEAVILY) on Pauses (Thread.Sleep was my friend). If I had spent just a few hours at the inception thinking about how to handle load time, it would have saved me says of re-factoring pain when I found out I'd be working more on this product than I initially thought.

STRATEGIES FOR HANDLING LOAD TIME:
1. Whatever scripting tool you are using (selenium, Test Complete, Mercury, VS Coded UI Tests) should have some way of managing timeout times. That is to say, how long it will wait for an element before it gives up and throws an error. If you are evaluating a scripting tool, DO NOT give up on it before you have researched this. Currently, I'm using Selenium RC (c#), here is the method available for this job:

driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));

This will make the driver search for an element for 10 seconds if it's not immediately present. Note: If you are using some code to measure time, having this set will corrupt your results. You can easily toggle this setting on and off in your script, so it shouldn't be a big deal.

2. Another commonly used tactic is to wait for another thing to happen before proceeding. This can be particularly useful as you're navigating from page to page, especially if you don't want to set your ImplicitWait as long as 10 seconds. Here's a snippet of code that I wrote that will wait for an element to be present:

        public static WaitForElement(string type, string value)
        {
            bool catchFlag = false;
            while (catchFlag == false)
            {
                catchFlag = true;
                try
                {
                    Thread.Sleep(500);
                    driver.FindElement(By.Id(value));
                }
                catch
                {
                    Thread.Sleep(500);
                    catchFlag = false;
                }
            }
       }

Notice that I like to put a half second buffer to account for page rendering time and general server funkyness. I know, I know. It's a crutch and I shouldn't need it.

3. The dreaded forced delay. This should be your last alternative, but that doesn't mean it should never happen. I think it's all about weighing the cost benefit of using a delay to handle loading. if you know that there is absolutely no chance you'll ever have to wait more than 5 seconds between two actions, and you've spent 6 hours trying to figure out how to catch the ready state in your script and you are still at square 1, then by all means.. use a delay! Just don't use them everywhre because you are LAZY! Delay's create brittle scripts that are not reliable. Having said all that, I found them essential on one part of the application I'm working on, where a postback is done, but NOTHING on the page changes.

Friday, November 18, 2011

(Simple) Performance monitoring using Selenium Remote Web Driver (c#)

Hello all,

I wanted a simple way to monitor extremely long load times on my site. I know this isn't 100% accurate, because it really won't measure page rendering, however I find this code useful for throwing an alert when something is lost in server-side loading hell (ie A GLOBAL SEARCH!).

This code will attempt to find an element on a page you are expecting to have loaded. The while loop will not stop until that element is found. 

Note that I use XPath to find my element, however with Selenium RC you are not limited to just XPath.

using System.Diagnostics; //for the stopwatch

public static void WaitForElement(string xPath){
            bool catchFlag = false;
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            while (catchFlag == false)
            {
                catchFlag = true;
                try
                {
                    driver.FindElement(By.XPath(xPath));                
                }
                catch
                {
                    Thread.Sleep(500);
                    catchFlag = false;
                }
            }
            stopwatch.Stop();
           if (stopwatch.ElapsedMilliseconds > 8000)
                  MessageBox.Show("Took too long! " + stopwatch.ElapsedMilliseconds + "ms");
}