Recently it has been my absolute pleasure to have been volunteered to automate a manual test suite for a web app built on Oracle Application Server.
Without a shadow of a doubt, this is the worst thing I have ever had to do professionally. The code generated by Oracle for this web app is so counter-intuitive and flies in the face of so many tenets of web development and common sense that I literally have to limit my exposure to it to 10 minute chunks so that I don't forget how the internet works.
As an example, you might expect each page to be built by requesting an HTML content page, which in turn requests scripts and stylesheets. OAS knows this is just what you are expecting, so it requests an HTML page that contains Javascript embedded in broken, duplicated, irrelevant and non-existent tags. The Javascript shamelessly builds a window containing a frameset (yes, this is in 2009), which is composed of several blank (containing only broken HTML tags) pages and some more HTML pages, which could contain Javascript or CSS or - in rare cases - HTML content. The system is entered via a log-in page which contains a few sentences of text, some graphics and a 2 field form with a submit button. To render this page, 14 separate page requests are made. 14!
As you can imagine, this does the system no favours whatsoever in terms of performance. I shudder to think what this means for whichever poor sucker is charged with maintaining this mess.
More importantly (for the job at hand), I was sceptical about the ability of any test tool to make sense of all this. My fears were borne out when TestComplete 7 puked all over the login page and locked up my machine - forcing a cold boot and subsequently 90 minutes of RAID array repair. Thanks, Oracle! Selenium at least managed to keep its lunch down, but could not make head nor tail of anything due to the hidden multiple pop-ups that go into displaying this rubbish.
Undeterred by reality, management insisted that I continue to automate the tests (because they had promised it could be done). My suggestion that the system be written properly first was given short shrift - no doubt due to its completely unmaintainable nature.
I decided quite early on that asking any automation tool to make sense of the DOM or any browser objects in the face of such industrial grade obfuscation was going to be fruitless. To test the functionality of the system, it would be enough to build VS2008 webtests to drive traffic to the server and take care of fitting form and query string parameters to the needs of each test.
I set about recording some webtests and these seemed to work. I wasn't keen on having to keep track of more than 2 dozen requests each time I clicked a menu item in the app, but needs must. They even seemed to play back OK (apart from some bizarrely hard coded image URLs on some pages).
With OAS, however, nothing is ever that simple. Coming back to the tests later, I started to see errors relating to non-existent sessions. I could see the session numbers being passed as query string parameters on certain pages, so I looked through each request to find where the session number was being set. Before I changed the webtest, I ran it again to check where it was failing. As you might now expect, the webtest ran without errors. Several times. Then failed. Several times. Bless you, Oracle!
Eventually I found the session number being set in the surprisingly clearly named "start_session" page. Hilariously this page contains an opening <HTML> tag, followed by a HEAD section containing FrontPage-quality boilerplate. Then a 4 line SCRIPT section comprising the declaration of one global variable, which contains the session number I need, 2 commented out lines and finally a line containing 2 empty loops in lieu of any proper understanding of event timing in Javascript. This closed out with 2 </SCRIPT> tags (presumably for robustness), no BODY section and no closing </HTML> tag. Say what you like about IE6 - the fact that it manages to render any of this makes it the greatest programming achievement in the history of the world.
<HTML>
<HEAD>
<META HTTP-EQUIV="Cache-control" Content="no-store">
<META HTTP-EQUIV="Pragma" Content="no-cache" max-age="0">
<META HTTP-EQUIV="Expires" Content="-1">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE></TITLE>
<SCRIPT>
var v_page = "ww_pa_generic.istandard_frames?p_param=LOGON_ENVNO=61&p_param=LOGOUT_MENU=Y
&p_param=BGROUP=H01&p_param=REFNO=0004802&p_param=APPTYPE=S&p_param=SD01X=0001
&p_param=SESSION_ID=PA524302:12&p_param=PRIMESESSNO=13340&p_param=LANG=|&p_param=LEVEL=1
&p_param=SESSION_TRACK=Y&p_title=AUT&p_heading=&p_sub_heading=
&p_proc_name=ww_pa.launch_concurrency_setup?p_param=LOGON_ENVNO=61%26p_param=LOGOUT_MENU=Y
%26p_param=BGROUP=H01%26p_param=REFNO=0004802%26p_param=APPTYPE=S%26p_param=SD01X=0001
%26p_param=SESSION_ID=PA524302:12%26p_param=PRIMESESSNO=13340%26p_param=LANG=|
%26p_param=LEVEL=1%26p_param=SESSION_TRACK=Y&p_home_ind=N";
// if (v_page.search("p_param") >=0)
// v_page = v_page + "&p_param=LANG=" + opener.parent.frames['menu_frame'].current.language ;
for(var i=0; i<70000; i++); parent.gl_winclose=1; parent.location.replace(v_page); for(var i=0; i<70000; i++);
</SCRIPT>
</SCRIPT>
</HEAD>
I'll just let that soak in - exquisite, isn't it? (Notice how badly confused the automatic code formatting has got by this point...)
Regardless of my perfectly justified need to continually slag off the code, at least I could now create an Extraction Rule in VS to rescue the session number:
WebTestRequest request8 = new WebTestRequest("http://oas.web.nightmare.xxx/pls/uatufa1/ww_pa_logon.start_session");
request8.Encoding = System.Text.Encoding.GetEncoding("us-ascii");
request8.QueryStringParameters.Add("p_param", "CONCUR%3dY", false, false);
request8.QueryStringParameters.Add("p_param", "LOGON_ENVNO%3d61", false, false);
request8.QueryStringParameters.Add("p_param", "LOGOUT_MENU%3dY", false, false);
request8.QueryStringParameters.Add("p_param", "BGROUP%3dH01", false, false);
request8.QueryStringParameters.Add("p_param", "REFNO%3d0004802", false, false);
request8.QueryStringParameters.Add("p_param", "USERID%3dPA524302", false, false);
request8.QueryStringParameters.Add("p_param", "APPTYPE%3dS", false, false);
request8.QueryStringParameters.Add("p_param", "CONCUR%3dY", false, false);
ExtractText extractionRule1 = new ExtractText();
extractionRule1.StartsWith = "PRIMESESSNO=";
extractionRule1.EndsWith = "&";
extractionRule1.IgnoreCase = false;
extractionRule1.UseRegularExpression = false;
extractionRule1.Required = true;
extractionRule1.ExtractRandomMatch = false;
extractionRule1.Index = 0;
extractionRule1.HtmlDecode = true;
extractionRule1.ContextParameterName = "";
extractionRule1.ContextParameterName = "PrimeSessNo";
request8.ExtractValues += new EventHandler(extractionRule1.Extract);
yield return request8;
request8 = null;
After that all I need to do is replace any PRIMESESSNO literals in Query String Parameters with the {{PrimeSessNo}} context variable and the webtests will now always run with valid sessions:
<QueryStringParameter
Name="p_param"
Value="PRIMESESSNO%3d{{PrimeSessNo}}"
RecordedValue="PRIMESESSNO%3d13294"
CorrelationBinding=""
UrlEncode="False"
UseToGroupResults="False" />
All I need to do is just this. For all requests with QSPs. Of which there are many. Far too many.
Still, as I'm constantly reminded - "this is chargeable work" - so who am I to complain?
Me. That's who.
What exactly is the point of testing code I already know to be of such staggeringly low quality? Especially when the development team have already committed to making no changes based on my findings. Some days I don't know why I bother.
A request to OAS generates multiple downloads of HTML formatted files:
Anyway, as it turns out, this wasn't such a problem as I first thought and it gave me an excuse for a technical deep dive into Visual Studio Coded Webtests