Monday, January 11, 2010

Silverlight UIAutomation Testing -- Using WatiN to navigate to a page and White to test a Silverlight app

I had the need recently to automate testing of a Silverlight app and ran into a problem using white (could anyone have come up with a more difficult name to search on?) where white does not provide functionality to get a reference to a hyperlink in the DOM and click it in the way that, for example, WatiN does. The scenario where this came up is there is a web app with a landing page that has a link to the page hosting the Silverlight app and we need to click through the link into the Silverlight hosting page.

I briefly thought about just cribbing the functionality from WatiN and extending white's capabilities but quickly discarded that idea as too complicated and out of concern that it would become a maintenance nightmare.

The solution I came up with is to use WatiN and white together in a hybrid fashion, where WatiN spins up a browser instance and then white attaches to that browser instance using the ProcessID of the instance from WatiN. White doesn't expose an Attach method up in the InternetExplorer class where Launch lives, but it does have it down in White.Core on the Application class, so it was a simple matter to create an extension method on the InternetExplorer class to expose one that can be used in unit tests.

It goes a little like this (warning: horrible formatting):

public static class WhiteExtensions
{
// the extension method to simplify usage
public static InternetExplorerWindow Attach(
this InternetExplorer val, int processId, string title)
{
InternetExplorerFactory.Plugin();

return (InternetExplorerWindow)Application
.Attach(processId)
.GetWindow(title);
}
}

// new up a browser with WatiN
Watin.Core.IE watin = new Watin.Core.IE(url);
watin.Link(Find.ByText("foo")).Click();

// spin up white and attach to the browser that WatiN started
White.WebBrowser.InternetExplorer white = new White.WebBrowser.InternetExplorer();
white.Attach(watin.ProcessID, "browser window title");

// get the Silverlight app reference from the white browser reference
White.WebBrowser.Silverlight.SilverlightDocument sl = white.SilverlightDocument;

// do stuff
sl.Get<Button>(SearchCriteria.ByAutomationId("SomeButton")).Click();


UPDATE: screenshot of exactly how my working unit test code looks

19 comments:

Anonymous said...

I went to school with a gal named Pamela Anderson. Needless to say, when I googled her the first few million hits weren't that Pamela Anderson.

Jeroen said...

Hi Leo,

Thanks for publishing the code! I added a link to your article here:

http://watinandmore.blogspot.com/2010/01/combine-watin-and-white-to-test.html

Jeroen van Menen
Lead dev WatiN

Leo said...

Thanks Jeroen!

And thanks for creating WatiN, it's a great tool and I use it quite a lot.

Anonymous said...

This is a great post, thank you so much for posting the code. I've recently started a project that is using a SilverLight plugin. I'm homing to leverage your solution that you described here. I've done a few projects now leveraging WatiN and I absolutely love it.

Any chance someone would like to take on the task of adding Silverlight testing functionality into WatiN? It would be nice to have a one stop shopping automation tool.

Anonymous said...

Leo,

I'm trying to implement this solution and stumbling upon an error. The code block:

public static class WhiteExtensions
{
// the extension method to simplify usage
public static InternetExplorerWindow Attach(
this InternetExplorer val, int processId, string title)
{
InternetExplorerFactory.Plugin();

return (InternetExplorerWindow)Application
.Attach(processId)
.GetWindow(title);
}
}

Produces this error:

Error 1 'White.WebBrowser.InternetExplorer': static types cannot be used as parameters


Did you experience this issue? Any idea on how to resolve it?


Thanks,

Leo said...

Hi John, sorry for the delay in responding to your question. It has been a while since I published this so it is not fresh in my mind, but I don't recall running into the issue you are seeing. I will see if I can reproduce the problem and then post a solution if I can.

Leo said...

OK, I took a look and I am running into a similar problem now, and it looks to be related to a change that I think must have been made to the white codebase that is causing my approach to break. Previously the InternetExplorer class in White.WebBrowser could be instantiated but it now is a static type, and in the quick poking around I did just now I did not see some other way of doing that, although it may be the case the the author who changed InternetExplorer to static provided some other way to get at an instance of that class and I just didn't see it when looking over the past few minutes.

I actually ran across an work item at codeplex reporting this exact problem (http://white.codeplex.com/workitem/9513) but as far as I can tell it has not been resolved.

I plan to chime in with a comment on the work item to see if I can help explain what the issue is.

Sherl said...

Hi Leo- I am having the same issue where I'am unable to navigate to a page and then access Silverlight controls. Could you please help me with this. Not sure how to combine White with WatiN to achieve this.

Leo said...

Hi Sherl,

I looked into this a few months back and if you follow the link I posted in my comment (http://white.codeplex.com/workitem/9513) you can see a work item that was entered at the project white codeplex site.

The issue is that between the time I created my solution and now, the author of project white modified the code in such a way that it prevents me doing what I was able to do before.

There are a couple of options:

1) If I have time, I can revisit my solution and see if I can craft a new solution using the new project white codebase

2) You (and others, hopefully) can appeal to the author at the link above to change the code back so that the previous behavior works again. You, or I, or others could offer to contribute our efforts to make those changes. From what I can tell on the codeplex site for this project, the author is unclear that there is a need for this, so that's why I suggest posting a comment there that hopefully he will see.

If all else fails and there were good reasons for it not getting reintroduced into the project white codebase, we still could add it back in ourselves and just share the updated code with one another and possibly the community. That would solve the immediate problem but feels like it could be a mistake in the long run because it would create a fork in the codebase that will lead to fragmentation.

Leo said...

OK, I figured out a solution: there no longer is a need for the extension method that I wrote previously, you now can Attach directly, so it is not an issue that the InternetExplorer class is static.

[TestMethod]
public void TestMethod1()
{
string url = "http://localhost[port#]/WatinWhiteTestLandingPage.aspx";

WatiN.Core.IE watin = new WatiN.Core.IE(url);

watin.Link(Find.ByText("click here)).Click();

InternetExplorerFactory.Plugin();

string title = "[browser title here]"; // will be something like "WatinWhiteHybrid - Internet Explorer provided by ..."

var ie = (InternetExplorerWindow)Application.Attach(watin.ProcessID).GetWindow(title);

White.WebBrowser.Silverlight.SilverlightDocument sl = ie.SilverlightDocument;

sl.Get(SearchCriteria.ByAutomationId("ClickMeId")).Click();
}

I wrote a sample app and referenced these versions of watin and white:

watin-2.0.50.1179.zip from 2011-02-08 http://sourceforge.net/projects/watin/files/WatiN%202.x/2.0%20Final/

white 0.20 Binaries http://white.codeplex.com/releases/view/29694

Anonymous said...

Hi Leo,

This is cool! Now I can update my SilverLight Control as well. This (WatiN) Control hides the attaching to White and Silverlight and exposes the White automation api. If I have time I'll write a blog post soon with the code for control. This way it looks (even) more integrated and becomes very easy to combine White and WatiN.

Thanks again!
Jeroen
Lead deve WatiN

Sherl said...
This comment has been removed by the author.
Leo said...

Jeroen -- great! Looking forward to seeing it.

Cheers,

Leo

Leo said...

Sherl -- I am seeing e-mail saying you are commenting but it shows as deleted here. Just in case you are trying to comment but blogger isn't letting you, I'll post a reply here.

It may be stating the obvious, but it sounds like you are not on the page with the Silverlight control at the time you are trying to grab a reference to it. Is this something that is straightforward to repro in a sample app?

Sherl said...

Hi Leo - In the application I am trying to automate (using WatiN), the silverlight is contained within a Div. Watin recognises the Div but not the Silverlight object contained with the Div.

It is similar to this:
div id="silverlightControlHost"
object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"
param name="source" value="ClientBin/SilverlightApplication1.xap"/
a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none"
img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/
/a
/object
/div

Thanks,
Sherl

Leo said...

Hi Sherl,

Just to be clear, you mean you are trying to automate testing using White, correct?

I have a sample app I created to get this new way working, and the fact that the Silverlight document is in a div isn't presenting any problem -- the SL app is hosted on a test aspx page that was generated when I created the test solution).

I'm going to add an image to my original post that has the code exactly as it looks in the code editor. I've used fully-qualified class names in case there has been any confusion around any of the classes in use.

Cheers,

Leo

John said...

Leo,

Nice job on the solution and thanks again for posting the code. I had opened the issue in codeplex back in September of 2010, it was just recently closed but I am not positive if the IEExplorer class is still static. Regardless, it looks like you have come up with a solution that does not require an extension. Nice work once again.

Thanks,

John

John said...

InternetExplorer class vice IEExplorer class, mistyped.

dhimanak said...

I'm trying to retrieve a silverlightdocument object using the sample code provided by you but for some reasons, it is throwing an exception UIItemSearchException in the white framework. Using all the latest libraries from Nuget. Please suggest.