Wednesday, February 22, 2012

Using Reflection to track page object actions

Some quick code I thought I would share.  This will use .Net reflection to return the Page Object function currently being executed.  It's just a nice way to track or log what action failed without having to resort to interception or some other technical wizardry.


 public static string GetPageObjectFunctionName()
        {
            string functionName = "";
            System.Diagnostics.StackTrace callStack = new System.Diagnostics.StackTrace();

            System.Diagnostics.StackFrame[] frames = callStack.GetFrames();
            foreach (System.Diagnostics.StackFrame frame in frames)
            {
                if (frame.GetMethod().ToString().Contains("PageObject"))
                {

                    functionName = frame.GetMethod().ReflectedType.Name + "." +  frame.GetMethod().Name.ToString();

                }

            }
            return functionName;
        }

Getting Console Errors From the Browser

So being able to grab Console errors out of the browser has long been a feature I wanted to add into our selenium framework.  Thanks to the help of a couple devs, I've finally worked out a nice way to grab console messages.  Basically the functionality works as follows:


First we override the Console object with some custom functionality every time the page loads.  We injected this into our Page Object base class, that all pages inherit from. This causes it to be called every time the page changes.  After we perform some actions on the page we check our object to see if any console messages have been seen.


There are several ways to do this, but we wanted to be able to differentiate between errors, warnings, logs, and uncaught exceptions.  This code will create the following variables on the page you can grab:

window.console.errorsJson (Errors)
window.console.warnsJson (Warnings)
window.errorsJson (Uncaught Exceptions)
window.console.logsJson (Logs)

First we need to override the console.  This is our magic function that performs all the work on the page.  The beauty of this function is that you can keep calling it multiple times and it won't delete the previous data.  
                string function = "var win = this.browserbot.getUserWindow();win.errors = win.errors || [];win.errorsJson = win.errorsJson || \"\";win.originalonerror = win.originalonerror || win.onerror ||\"none\";win.onerror = function(errorMsg, url, lineNumber) { win.errors.push({\"errorMsg\": errorMsg || \"\",\"url\": url || \"\",\"lineNumber\": lineNumber || \"\"});if (JSON &&JSON.stringify) win.errorsJson = JSON.stringify(win.errors); if (win.originalonerror != \"none\") win.originalonerror(errorMsg, url, lineNumber);};win.console = {logs: win.console.logs || [],logsJson: win.console.logsJson || \"\",log: function() {win.console.logs.push(arguments);if (JSON && JSON.stringify) win.console.logsJson = JSON.stringify(win.console.logs);},warns: win.console.warns || [],warnsJson: win.console.warnsJson || \"\",warn: function() {win.console.warns.push(arguments); if (JSON && JSON.stringify) win.console.warnsJson = JSON.stringify(win.console.warns);},errors: win.console.errors || [],errorsJson: win.console.errorsJson || \"\",error: function() { win.console.errors.push(arguments);if (JSON && JSON.stringify) win.console.errorsJson = JSON.stringify(win.console.errors);}};";
selenium.GetEval(function);

Now we can get our messages like this:

string errorFunction = "this.browserbot.getUserWindow().console.errorsJson";
string errors = GetEval(errorFunction);
                if(errors!="")
                    TestLog.WriteLine("Console errors found: \r\n" + errors);
       
There you go: Console message logging.  We can of course include other functionality, like throwing an exception if an error occurs, or performing another action.   All we have to do to integrate this into our framework is to perform both actions in our page object base class's constructor, and each page object will automatically call it.