In recent work I was investigating the possibilities of testing a system that was not necessarily built to support unit tests. The system is lacking any true component structure that would allow MXUnit tests to be created against the code. This is where Selenium comes in. After some investigation I figured out that I could use Selenium to test the system. Then taking it a step further I could then use CFSelenium with MXUnit.

In the event you didn't know, CFSelenium (http://www.silverwareconsulting.com/index.cfm/2011/2/22/Introducing-CFSelenium--A-Native-ColdFusion-Client-Library-for-SeleniumRC) is a ColdFusion wrapper to run Selenium Server. This will allow you to run Selenium tests without the need to use the Selenium IDE. Also, since we are writing tests in ColdFusion, we can add dynamics and other things to better exercise the interface. We can then wrap the tests in MXUnit test cases for a complete well rounded experience. My first exercise was I wanted to be able to auto start and stop the Selenium engine for each test suite. I didn't want to have the Selenium engine always running in a console on the server. Thankfully, CFSelenium comes with a way to do that. Here is a basic MXUnit test that is using CFSelenium.

view plain print about
1component extends="cfselenium.CFSeleniumTestCase" displayName="Untitled" {
2
3 public void function beforeTests() {
4 browserUrl = "http://www.google.com";
5 super.beforeTests();
6 selenium.setTimeout(30000);
7 }
8    
9    public void function testUntitled() {
10     selenium.open("/");
11 selenium.waitForPageToLoad("30000");
12 }
13
14    function afterTests() {
15     super.afterTests();
16 }
17}

As you can see we extend to the CFSeleniumTestCase. This gives us some base controls for CFSelenium. Mostly the before and after tests functions for CFSelenium. Now, don't get them confused with the beforeTests() and afterTests() functions in MXUnit.

In our MXUnit test we run beforeTests(). This then runs super.beforeTests(). This is the function in the CFSelenium cfc. This function basically creates the CFSelenium object and starts the Selenium server if it is not running.

Looking at the server.cfc file in the cfselenium directory, there is a startserver function. Here I made a slight modification to how the Selenium server actually starts. Granted I didn't have to modify it but I wanted to add some new functionality that wasn't possible using cfexecute.

Here is the original code

view plain print about
1<cfexecute name="java" arguments="#args#"/>

Here is my change:

view plain print about
1<!--- Get Java's runtime object which lets us execute system commands. --->
2<cfset runtime = createObject("java", java.lang.Runtime").getRuntime()>            
3<!--- Execute the command. --->
4<cfset process = runtime.exec(args)>

Now that I had Selenium server running in this fashion, I could add a little more functionality to it. Now, after the runtime.exec() command I added the following code

This allows me to capture the output that would normally be seen in the console. Since the errors come back in a different stream I have to create a second thread to capture those. There is no need to write any code to terminate the threads. These threads will end on their own when the Selenium server stops.

Lastly I create the ProcessStream function to do something with the stream.

view plain print about
1<cffunction name="processStream">
2    <cfargument name="catpureThread" type="any">
3    <cfset streamReader = createObject("java", "java.io.InputStreamReader").init(arguments.catpureThread)>
4    <cfset buffererdReader = createObject("java", "java.io.BufferedReader").init(streamReader)>
5    
6    <cfloop condition="true">
7    
8        <cfset line = buffererdReader.readLine()>
9    
10        <cfif not isDefined("line")>
11            <cfbreak>
12        </cfif>
13    
14     <cfset logStatus( text="-- #line#" )/>
15    
16    </cfloop>
17    
18</cffunction>

This uses some java objects to capture the thread output stream. The loop will continue forever continuously reading line after line of the stream. The loop will eventually stop once the buffer reader stops returning. This would happen when Selenium server stops. I then take the line output and use the pre-existing logStatus function to write it to the log. This now allows me to see the raw output from Selenium. This allows me to investigate any issues with the tests as well as look for any other issues.

Till next time,

--Dave