Unit Testing Silverlight with Selenium
Selenium is a web testing tool that runs directly in the browser. That means it's all JavaScript. It'll run on the Three Major OSes and on the Three Major Browsers (and others!), so that's cool. There's even a recorder/playback IDE called Selenium IDE that takes the form of a Firefox plugin.
One of the things that's particularly interesting about Selenium is that it uses HTML Tables as its input Domain Specific Language. Go ahead and read that sentence again just to drink it in.
For example, if you had a page called "/default.html" and on that page there was a button called "myButton" here's the code to open the page and click that button.
<table cellpadding="1" cellspacing="1" border="1"> <thead> <tr><td rowspan="1" colspan="3">SeleniumTest</td></tr> </thead><tbody> <tr> <td>open</td> <td>/Default.html</td> <td></td> </tr> <tr> <td>click</td> <td>myButton</td> <td></td> </tr> </tbody></table>
This would be a "test" page and you'll also need a master "Suite" that would contain many tests. A Suite is just a table of tests:
<table> <tr><td><b>Suite Of Tests</b></td></tr> <tr><td>
<a href="./SeleniumTest.html">Test Suite</a>
</td></tr> </table>
Because of cross-site scripting issues, you have to install Selenium (just download the zip and upload the contents to your website) on your server and visit the pages from there. For example, here's the Selenium Test Runner on my server here at Hanselman.com.
As Selenium is all JavaScript, I thought it'd be nice to make sure it can test Silverlight applications. I can see how a testing framework that could easily interact with Silverlight would be a useful thing, so I set off to prove the concept.
First, I made a super-simple XAML of a TextBlock and a Circle:
<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ButtonSharp="clr-namespace:ButtonSharp;assembly=ClientBin/ButtonSharp.dll" x:Class="ButtonSharp.Page;assembly=ClientBin/ButtonSharp.dll" Width="320" Height="240" Background="White" x:Name="rootCanvas" > <Ellipse Fill="#FFFFFFFF" Stroke="#FF000000" x:Name="Circle"
Width="169" Height="169" Canvas.Left="81" Canvas.Top="32"/> <TextBlock x:Name="MyText" Text="Hello World from my Script Tag"/> </Canvas>
In the code-behind I made a few Scriptable methods. By marking them Scriptable, they are made available to the "outside world" - in this case, JavaScript.
[Scriptable] public partial class Page: Canvas { public Page() { Loaded += new EventHandler(Page_Loaded); } TextBlock tb = null; Ellipse el = null; void Page_Loaded(object o, EventArgs e) { WebApplication.Current.RegisterScriptableObject("scott", this); tb = (TextBlock)this.FindName("MyText"); el = (Ellipse)this.FindName("Circle"); } [Scriptable] public void SetCircleColor() { tb.Text = "Hello from C#"; el.Fill = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00)); } [Scriptable] public string GetCircleColor() { SolidColorBrush b = el.Fill as SolidColorBrush; return b.Color.ToString(); } }
The code does two useful things. First, it registers the class with the WebApplication via RegisterScriptableObject and names it "scott." You'll see later where we refer to "scott" in JavaScript and you'll know we mean this class. Second, it finds the TextBlock and the Ellipse and squirrels them away for later use.
Back over on the outside, in the HTML page's JavaScript, I make two silly functions:
<script type="text/javascript"> function changeColor() { var control = document.getElementById("SilverlightControl"); control.Content.scott.SetCircleColor(); } function getColor() { var control = document.getElementById("SilverlightControl"); thing = control.Content.scott.GetCircleColor(); alert(thing); } </script>
Notice where we call the two managed C# methods via our "scott" object. Then I add two regular HTML buttons:
<input type="button" name="changeColor" value="Change Color" onclick="changeColor()"/> <input type="button" name="checkColor" value="Check Color" onclick="getColor()"/>
You click one button and it calls changeColor which should make make the Circle red, and the other will ask the Circle what color it is and display the result in a JavaScript alert. You're welcome to run this little thing here if you like.
Note that you WILL need the Silverlight 1.1 Alpha Refresh first.
Now I make a Selenium Test like this. You can also use the Selenium IDE to record, write manually, step through and save tests if you're not into the whole Notepad thing.
<table cellpadding="1" cellspacing="1" border="1"> <thead> <tr><td rowspan="1" colspan="3">SeleniumTest</td></tr> </thead><tbody> <tr> <td>open</td> <td>http://www.hanselman.com/silverlight/seleniumtest/ButtonSharp/Default.html</td> <td></td> </tr> <tr> <td>click</td> <td>changeColor</td> <td></td> </tr> <tr> <td>click</td> <td>checkColor</td> <td></td> </tr> <tr> <td>assertAlert</td> <td>#FFFF0000</td> <td></td> </tr> </tbody></table>
The Selenium Reference is excellent and contains all the commands you can use. Remember, each command is one line in test table like:
command target value
And there's many you can use. You can also make your own by making a file called user-extensions.js, and there's lots of details on the Selenium site. You can also use actual JavaScript in the targets and values if you include 'javascript{ }' in the parameter.
I'm sure that if I knew JavaScript better I could better access the Silverlight object and test all kinds of things. I'll leave this exercise to the Smarter Than I Reader.
You can pass parameters to the Selenium Test Reader like the test file via ?test= etc. If you want to run my Silverlight App inside the Selenium Test Runner in your own browser, just go here. Do note the format of the URL. You can also just visit the runner and enter the URLs yourself. You can have as many tests as you like in the suite.
NOTE: When you startup the runner, change these settings for a more dramatic experience. Turn the speed down to Slow with the slider, and click Highlight elements. Then click either of the Green Play Buttons.
It'd be interesting what kinds of community extensions to Silverlight could be created for Selenium to make it even easier. I also wonder how well Watir or Watin can interact with Silverlight.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
I too just posted a write-up on using Selenium. Thanks for adding some "color" regarding using it with Sliverlight. I haven't yet had the opportunity to put Sliverlight to use though the time is coming so this post will come in handy.
Here's my post:
Automated testing of ASP.NET web applications using Selenium
We're evaluating it now. Integrates with NUnit, MSTest, etc... Supports javascript, Ajax testing, IE and Firefox. Also makes it easy to test using Cassini. Check out the feature list. It *looks* like it puts WATIN to shame. I'll let you know more as we try it out.
Oh, and it's FREE!
Cheers
Harald
We will very likely start using Silverlight in specific areas of our site over the next couple of years. We will use it for creating true Rich Internet Applications (RIAs), which means that the users (and our tests) will be interacting directly with the XAML DOM. One of the things I've yet to see is elegant support in any of the automated UI test tools for interacting with and asserting properties of elements on the canvas. While I haven't delved too deeply into this, I wonder if it wouldn't make sense to create an implementation of the ISelenium interface specifically for Silverlight (see http://release.openqa.org/selenium-remote-control/0.9.0/doc/dotnet/html/index.html, and drill down into the DefaultSelenium class, which is currently the only implementation of ISelenium).
Thanks again,
-=michael=-
Aside from documentation provided on Open QA forums. Are there any up and coming training classes to better understand Selenium (IDE/testrunner/RemoteControl).
Thank You,
Pardip
Comments are closed.