ZeroSharp

Robert Anderson's ones and zeros

Load Testing XAF: Part 2 - Selenium

| Comments

Writing a Selenium User Test against MainDemo

This is another post in a series about load testing XAF applications. Previously in the series:

Why not use DevExpress EasyTests?

The DevExpress recommended method of writing functional tests is to use the EasyTest functionality of the expressAppFramework. This has several advantages over other functional testing approaches.

  • It uses a domain specific language tailored for XAF making it easy to test views and actions
  • It makes it easy to interact with the DevExpress controls that are used within XAF
  • A single EasyTest can be run against both the ASP.NET and WinForms applications
  • EasyTests work against both the debug webserver and IIS

However, one feature which is not (yet) available is the ability to use EasyTests for load testing.

UPDATE: See my more recent post on how run multiple simultaneous EasyTests.

Modifications to the MainDemo

The sample script I have written assumes the MainDemo is running with Horizontal Navigation rather than vertical. You can modify the script to add support for vertical navigation or you can change Global.asax.cs Application_Start as follows:

1
2
3
4
5
6
7
8
protected void Application_Start(object sender, EventArgs e)
{
    RenderHelper.RenderMode = DevExpress.Web.ASPxClasses.ControlRenderMode.Lightweight;
    ASPxWebControl.CallbackError += new EventHandler(Application_Error);

+    // Add the following line to default to horizontal layout
+    WebWindowTemplateHttpHandler.PreferredApplicationWindowTemplateType = DevExpress.ExpressApp.Web.Templates.TemplateType.Horizontal;
}

The Selenium script

Selenium is a powerful tool for automating browsers. It supports all of the major browsers and a Selenium test can be written in many different programming languages (C#, Java, Javascript, HTML, etc.) The load testing tool (which we will come to in part 3 of this series) uses Selenium scripts written in Javascript.

We will now create and verify a simple Selenium test. The test will open the browser, login to the MainDemo and cycle through all of the tabs before logging out. The script is extremely basic. For a more realistic load test, you want a combination of scripts running, some entering data, some triggering reports, etc.

Create a \scripts subdirectory and populate it with the following code:

MainDemo_CycleThroughTabs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* global test */

// Settings for Neustar:
// replace the following with the public address of the application server,
var targetHost = "http://zerosharp-maindemo.elasticbeanstalk.com/";
var virtualShare = "MainDemo.Web_deploy";

// Settings for debug webserver:
// (local script validator doesn't always work against localhost,
// so we use the excellent localtest.me instead.)
//var targetHost = "http://localtest.me:58404";
//var virtualShare = "";

// Settings for the build server or IIS:
//var targetHost = "http://localtest.me/";
//var virtualShare = "MainDemo.Web";

// Test parameters
var thinkTimeInSeconds = 3;
var timeout = 60000;
var step = 0;

// You an optionally set the simulated bandwidth for the script
// (max of 100KB/sec). A value of -1 means do not limit.
// E.g., 
// var bandwidthLimit = 50 * 1024 * 8; // 50KB/sec
var bandwidthLimit = -1;

var driver = test.openBrowser();
var selenium = driver.getSelenium();

// Support functions
function think() {
    if (thinkTimeInSeconds > 0) {
        if (!test.isValidation()) {
            test.pause(thinkTimeInSeconds * 1000);
        }
    }
}

function waitForCallbacks() {
    selenium.waitForCondition("(typeof selenium.browserbot.getUserWindow().xafHasPendingCallbacks === 'function') && (selenium.browserbot.getUserWindow().xafHasPendingCallbacks() === false);", timeout);
}


function stepLogin(username) {
    step = step + 1;
    test.beginStep("Step " + step.toString() + " - Login");
    selenium.open(targetHost + virtualShare + "/Default.aspx");
    think();
    selenium.type("xpath=//input[contains(@id,'_xaf_dviUserName_Edit_I')]", username);
    selenium.type("xpath=//input[contains(@id,'_xaf_dviPassword_Edit_I')]", "");
    selenium.click("Logon_PopupActions_Menu_DXI0_T");
    selenium.waitForPageToLoad(timeout);
    waitForCallbacks();
    selenium.assertElementPresent("Horizontal_VCC_VSL");
    selenium.waitForText("Horizontal_VCC_VSL", "Contact");
    test.endStep();
    think();
}

function stepLogoff() {
    var expectedSubstring;
    step = step + 1;
    test.beginStep("Step " + step.toString() + " - Logoff");
    selenium.click("//li[@class='dxm-item']/div[@class='dxm-content dxm-hasText']//a[@class='dx dxalink' and text()='Log Off']/..");
    selenium.waitForPageToLoad(timeout);
    expectedSubstring = "Logout.html";
    test.endStep();
}

function stepNavigateToTab(maintabCaption, tabCaption, viewCaption) {
    // viewCaption is optional
    viewCaption = (typeof viewCaption === "undefined") ? tabCaption : viewCaption;
    step = step + 1;
    test.beginStep("Step " + step.toString() + " - " + tabCaption);
    selenium.waitForElementPresent("//td[@class='dxtc' and text()='" + maintabCaption + "']");
    if (selenium.isVisible("//td[@class='dxtc' and text()='" + maintabCaption + "']")) {
        selenium.click("//td[@class='dxtc' and text()='" + maintabCaption + "']");
    }
    selenium.waitForElementPresent("//div[@class='dxm-content dxm-hasText' and starts-with(@id, 'Horizontal_NTAC_PC_M')]//a[@class='dx dxalink' and contains(text(), '" + tabCaption + "')]/..");
    selenium.click("//div[@class='dxm-content dxm-hasText' and starts-with(@id, 'Horizontal_NTAC_PC_M')]//a[@class='dx dxalink' and contains(text(), '" + tabCaption + "')]/..");
    waitForCallbacks();
    selenium.assertElementPresent("Horizontal_VCC_VSL");
    selenium.assertText("Horizontal_VCC_VSL", viewCaption);
    test.endStep();
    think();
}

function initializetest() {
    selenium.setTimeout(timeout);
    if (bandwidthLimit > 0) {
        test.setSimulatedBps(bandwidthLimit);
    }
}

(function main() {
    initializetest();

    test.beginTransaction();

    stepLogin("Sam");
    //stepNavigateToTab("Default", "Contact");
    stepNavigateToTab("Default", "Task");
    stepNavigateToTab("Default", "Department");
    stepNavigateToTab("Default", "Scheduler Event");
    stepNavigateToTab("Default", "My Details", "User - Sam");
    stepNavigateToTab("Default", "Note");
    stepNavigateToTab("Default", "Payment");
    stepNavigateToTab("Default", "Position");
    stepNavigateToTab("Default", "Resume");
    stepNavigateToTab("Default", "Role");
    stepNavigateToTab("Default", "User");
    stepNavigateToTab("Reports", "Analysis");
    stepNavigateToTab("Reports", "Reports");
    stepLogoff();
    test.closeBrowser();

    test.endTransaction();
}());

Neustar

In a future post we will create multiple test runners in the Amazon cloud using the Neustar web performance tool (formerly BrowserMob). Neustar will gather statistics about each scripts reponse times and provide a load test report including details of any test failures.

For now we will verify locally that the Selenium script above works as expected.

Installing the Neustar local script validator

In order to verify that our script is supported by the Neustar framework, we need to install their local script validator. Download it and unzip it to a subdirectory of the MainDemo.

There are instructions for setting up local script validation here.

To run the script locally call the following:

> script-validator-4.8.81\bin\validator.bat CycleThroughTabs.js -keepbrowseronerror

I had some problems getting the NeuStar script validator to work in 64-bit Windows 8. The script validator instructions recommend FireFox 12 but I am using version 19. For the record I am using:

  • DevExpress MainDemo 12.2.7
  • NeuStar localscriptvalidator 4.8.81
  • Mozilla FireFox 19
  • Java 7.0.90

You need to modify your C:\Users\<Username>\.wpm\config.properties file as follows:

config.properties
1
FF=C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe

Also, for some reason, I could not get the local script validator to run against localhost. I kept getting the error:

1
2
3
4
WARN 03/28 12:38:28 b.n.w.a.s.JavaScrip~ - Got script exception
org.mozilla.javascript.WrappedException: Wrapped biz.neustar.webmetrics.agent.ap
i.HttpErrorException: No valid HTTP Response received while navigating to URL 'h
ttp://localhost:58404/Default.aspx' (CycleThroughTabs.js#50)

The easiest solution was to change the localhost address in the javascript file to the excellent localhost alternative localtest.me.

Now when I run the script using the local validator with

> validator cyclethroughtabs.js

I see Firefox startup after a few seconds and the script correctly cycles through all of the tabs and then exits.

We will use this scenario as the basis of a load test in the next post.

Comments