Flow testing with WatiN
7 June 2007 in .Net, Code | Comments enabled

Last night I had the opportunity to speak to the Christchurch .NET User Group about web standards and took the opportunity to add some discussion about testing using a tool called WatiN (pronounced “what-in”, horrible I know). From the feedback I’ve received so far the WatiN demo captured everyone’s imagination. I had a great time as well, very friendly folks in Christchurch :)

WatiN effectively allows you to script interactions with Internet Explorer and do things like fill out forms, click through and test the results that come back. This makes testing things such as a process flow really easy to do in a repeatable manner and included in your unit tests which ideally are being executed as part of your continuous integration processes.

Examples:

The example I demonstrated last night was just doing a simple test of the BackgroundMotion search engine. You can probably understand what is going on just from the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
    [Test]
    public void TestSearchWithNoResult()
    {
      IE ie = new IE();
      ie.GoTo("http://backgroundmotion/");
      ie.TextField(Find.ById("ctl00_SearchBox")).Click();
      ie.TextField(Find.ById("ctl00_SearchBox")).TypeText("beach");
      ie.Button("ctl00_Search").Click();
 
      Assert.IsTrue(ie.ContainsText("No results found"));
 
      ie.Close();
    }

Automated Validation is a hot topic for me as I really hate working on a project and then remembering to validate well after I wrote the HTML only to find I have a lot of work ahead of me. I wrote the following test code to allow automated posting of web content to the W3C validation service for checking:

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
    private static bool IsValidPage(string url)
    {
      string rawHtml = GetFile(url);
      IE ie = new IE("http://validator.w3.org/fragment-upload.html");
      ie.GoTo("http://validator.w3.org/fragment-upload.html");
      ie.TextField(Find.ByName("fragment")).Click();
      ie.TextField(Find.ByName("fragment")).Value = rawHtml;
      ie.Button(Find.ByValue("Validate this document")).Click();
 
      return ie.ContainsText("Is Valid");
    }
 
    public static string GetFile(string url)
    {
      WebRequest myWebRequest = WebRequest.Create(url);
      WebResponse myWebResponse = myWebRequest.GetResponse();
      Stream ReceiveStream = myWebResponse.GetResponseStream();
      Encoding encode = Encoding.GetEncoding("utf-8");
      StreamReader readStream = new StreamReader(ReceiveStream, encode);
 
      string response = readStream.ReadToEnd();
 
      readStream.Close();
      myWebResponse.Close();
 
      return response;
    }

With these two methods you can do write a test per page to validate the content:

1
2
3
4
5
    [Test]
    public void TestValidHTML()
    {
      Assert.IsTrue(IsValidPage("http://www.mysite.com/mypage.aspx"));
    }

Note that this code doesn’t take into account subtle changes with the use of AJAX, it simply gets the raw content from the request. WatiN does however support handling AJAX changes quite nicely so upgrading to support a richer website would not present much of an issue. The interesting thing with the raw request is we could change the agent-type on the request to ensure that we’re getting valid HTML for various browsers despite being called from one central location. This can be great if you’re working with ASP.NET where the server controls will be rendered differently based on the agent type by default.

The code also doesn’t take account of potential network connectivity issues and was more for showing the sort of things that you might want to do with WatiN. WatiN is certainly a useful tool to have in your toolbox. If you want to learn more about WatiN just pull it down and also grab one of the few WatiN recorders as the one listed below and get a kick start in scripting your site interactions.

The Tools

WatiN: http://sourceforge.net/projects/watin/

WatiN Recorder: http://watintestrecord.sourceforge.net/

Hope that helps,

- JD


31 comments. Add your own comment.

KTC says 8 June 2007 @ 14:35

2 (possibly stoopid) Q’s from an uninformed guy

1.) So. Unit Tests. You write the tests. You write the code to satisfy the tests. You refactor the code, and the unit tests confirm the code still sort of does wehat it did before. But you don’t modify the tests unless you’re sure the code will break the tests. The essence of that being the tests are not tightly coupled to the code begin tested.

How do you avoid that with UI stuff like watin? Markup changes like buggery. Control tree’s are constantly refactored during dev, but the test code relies on some aspect of the markup being constantly identifiable and i can’t always see that would be the case – so you’d constantly refactor the tests to reflect this. Do you bring in the tests at a later stage?

2) Can watin handle stuff thats nestled 3 deep in naming containers? Do you just have to find something on the controls that can be leveraged by the find (or whatever the method was called) overrides?

Jeroen van Menen says 9 June 2007 @ 01:31

Hi JD,

Nice to read you did a presentation about WatiN and that it went well. And the example you gave is an intresting way to use WatiN (users keep surprising me in ways they utilyse WatiN).

In response to KTC’s questions, here are my answers:

1) A simple solution is to seperate testcode and the way elements are found on a page. for instance

[Test]
public void Example()
{
Page1 page1 = new Page1();
ie.TextField(page1.PasswordField).TypeText(“”);
….
}

public class Page1
{
public Attribute PasswordFiels()
{
return Find.ById(“pswdid”);
}
}

This way, no need to change your tests and only one place you define the attribute to find the element. Ofcourse you can make this more advanced. Search CodePlex on WatiN for an example app of Bruce McLeod. Or google for Richard Griffin and WatiN, He described a layered approach as well.

2) ie.Button(Find.ByText(“Delete”)) will look in the collection of all element on the page. So no need to define the hierachy the element is in. If you need to because your element has only something unique within, lets say a div, then you good reduce the search scope like this:
ie.Div(“someDivId”).Button(Find.ByText(“Delete”))

traskjd says 9 June 2007 @ 13:40

Jeroen, thanks for dropping by and passing on your answers to KTC (it was also helpful for me).

In terms of automating the testing the one thing I found as a small issue but was more because of my limited playing was how you can get the entire HTML document out of WatiN. I used the raw WebRequest because then I can impersonate any browser however a more light weight manner would be to get the raw response that WatiN received. Looking at the code the HTML property reads the Body property off the MSHTML object which is why it doesn’t provide the entire page.

I haven’t had a further look yet but is there a way of getting the entire html response nicely from WatiN?

I’ve found WatiN to be a fantastic tool, I’m glad you guys are getting some visibility for your work in porting Watir.

Thanks for dropping by,

– JD

Jeroen van Menen says 13 June 2007 @ 08:56

Hi JD,

I have had some other requests for this too (even recieved code to add to WatiN). So I’ll put this on the feature request list.

Enjoy testing with WatiN,

Jeroen

Rajasekar says 13 July 2007 @ 00:32

Please help me about how to save the test result… We are writing and executing the test with number of testcases and where will i save to test results..?

traskjd says 13 July 2007 @ 09:47

What do you mean by saving the test result Rajasekar?

With unit testing effectively you just fail a test and the details of why the assertion failed are detailed with whatever tool you’re using (or raw xml output from a tool like nunit).

Could you provide me with some more information?

Thanks,

– JD

Frf says 5 October 2007 @ 03:06

Hi.Tell me,pls how i can get a value of some attributes(href for example) when there’s no unique div name or some other unique name.Here’s the code:

Div division = ie.Div(Find.ById(“res”));
string o = division.Divs[0].Divs[0].Elements[1].-??;

How can I get into o the value of Elements[1].href, when Elements[1] is ?

traskjd says 8 October 2007 @ 09:51

Hi Frf,

I’m not too sure off the top of my head sorry. Perhaps if somebody finds out that is monitoring this thread they can post an answer.

If you’re testing the page output then you must have some control over it, why don’t you add an identifier?

– JD

Jeroen van Menen says 8 October 2007 @ 10:04

Hi Frf,

There are two options:

1 – Use the Elements[1].GetAttributeValue(“href”);

2 – Link link = (Link)division.Divs[0].Divs[0].Elements[1];
string o = link.Url;

traskjd says 8 October 2007 @ 10:08

Thanks Joroen :)

– JD

Frf says 8 October 2007 @ 21:03

THX,Jeroen

Frf says 20 October 2007 @ 00:50

Hello again.I wonder if WatiN supports pop-up windows? I tried to make a simple test of Yahoo mail service. When deleting a letter there, a dialog appears,asking if you really want to delete letter.How can I make my test to deal with this (and alike) dialogs?

Jeroen van Menen says 20 October 2007 @ 01:30

Have a look at the DialogHandlers in WatiN. These offer you the possibility of handling all kinds of pop-up dialogs (like alert, confirm, login, print etc..). The ie.HTMLDialog(findBy) lets you deal with modal or modeless html windows (the onces that show an html page but without a toolbar). The following code shows how to handle a confirm dialog:

public void ConfirmDialogHandlerOK()
{
using (IE ie = new IE(TestEventsURI))
{
ConfirmDialogHandler confirmDialogHandler = new ConfirmDialogHandler();
ie.DialogWatcher.Add(confirmDialogHandler);

ie.Button(Find.ByValue(“Show confirm dialog”)).ClickNoWait();

confirmDialogHandler.WaitUntilExists();
confirmDialogHandler.OKButton.Click();

ie.WaitForComplete();
ie.DialogWatcher.Remove(confirmDialogHandler);
}
}

HTH
Jeroen

Frf says 20 October 2007 @ 02:20

This code doesn’t work, I tried it.Here is the snapshot of the dialog:
-
http://www.flickr.com/photos/15421264@N05/1635711944/
-

Jeroen van Menen says 20 October 2007 @ 02:31

Thanks for the snapshot! This an HTMLDialog. Automate it like this (add using System.Text.RegularExpressions):

HTMLDialog confirmDialog = ie.HTMLDialog(Find.ByUrl(new RegEx(“ConfirmationDialog.html$”)));

confirmDialog.Button(Find.ByValue(“Delete”)).ClickNoWait();

ie.WaitForComplete();

HTH,
Jeroen

Frf says 24 October 2007 @ 03:05

Thanks.But this doesn’t work either.I suppose I have to add Wait….() methods to my code to make it working correctly.I even tried to use AttachToIE.In this case the TimeoutException is thrown.The code looks like this:
————————————
ie.Button(“deletetop”).ClickNoWait();
ie.WaitForComplete();
IE webDialog = IE.AttachToIE(Find.ByTitle(new Regex(“Webpage Dialog$”)));
webDialog.Close();
————————————

Jeroen van Menen says 24 October 2007 @ 08:25

Did you try what happened without the ie.WaitForComplete. (The reason you use ClickNoWait is that WatiN (internally) doesn’t call WaitForComplete after the click).

Can you figure out the javascript code that creates this dialog (probably in an eventhandler of the delete button)? That might help in solving the problem.

Maybe we should continue this discussion on the WatiN users mailing list (subscribe here https://lists.sourceforge.net/lists/listinfo/watin-users).

Gemo says 28 November 2007 @ 19:40

Hi Jeroen van Menen,

I m in need of ur help.

Here s my problem,

I m trying to catch a javascript confirm dialog box in Watin using ConfirmDialogHandler.

But i m getting the following error…

“Operation is not available. Dialog doesno’t exists”

Here goes my Script & code behind,

******************************************************

function OpenWindow()
{
var response = confirm(“Hi Gemo”);

}
******************************************************

Click
******************************************************

ConfirmDialogHandler confirmBox = new ConfirmDialogHandler();
ie.AddDialogHandler(confirmBox);
ie.DialogWatcher.Add(confirmBox);
ie.Link(Find.ById(“YouOK”)).Click();

using (new UseDialogOnce(ie.DialogWatcher, confirmBox))
{
confirmBox.WaitUntilExists();
confirmBox.OKButton.Click();
ie.WaitForComplete();
}
******************************************************

Lucy says 29 November 2007 @ 05:34

Hi Jeroen van Menen,
I need your help.Here is my problem:
I have a confirm window, once the window shows up, it waits for manually click OK, then I got error msg: Operation is not available. Dialog doesn’t exists. Even I comment out this line in section 1: //confirmDialogHandler.WaitUntilExists();
Is anyway I can catch the popup window in code behine and use like the following code section 2 to work around?
Section 1
Here is my Script & code behind:
*******
function DeletedFuction
{
var doDelete = confirm(“Delete it?”);
}
*******
ConfirmDialogHandler confirmDialogHandler = new ConfirmDialogHandler();
ie.AddDialogHandler(confirmDialogHandler);
ie.DialogWatcher.Add(confirmDialogHandler);
ie.Button(Find.ByValue(“Delete”)).
confirmDialogHandler.WaitUntilExists();
confirmDialogHandler.OKButton.Click();

ie.WaitForComplete();
ie.DialogWatcher.Remove(confirmDialogHandler);
***************
Section 2
NameValueCollection eventProperties = new NameValueCollection();
eventProperties.Add(“KeyCode”, “13″);
ie.ActiveElement.FireEvent(“OnKeyPress”, eventProperties);

Thanks for your help in advance.

Jeroen van Menen says 29 November 2007 @ 10:10

Hi Gemmo (and Lucy),

The code should look like this
to handle confirm dialogs:

ConfirmDialogHandler confirmBox = new ConfirmDialogHandler();

using (new UseDialogOnce(ie.DialogWatcher, confirmBox))
{
ie.Link(Find.ByText(“Click”)).Click();
confirmBox.WaitUntilExists();
confirmBox.OKButton.Click();
}

ie.WaitForComplete();

HTH,
Jeroen

Frf says 1 December 2007 @ 05:38

Hello.BTW thanks for answering my question.The last advice helped.
Now I have to deal with File Upload dialog.
——-
ie.Element(Find.ByName(“reportTemplate”)).Click();
ie.DialogWatcher.Add(new FileUploadDialogHandler(“one.txt”));
——-
This code works, but, to be honest,not in the way it’s expected to.So here are questions:
1.Handler handles the dialog, but only if Element is clicked for the second time.At first the dialog appears and then hides immediately.Using of ClickNoWait() doesn’t change the situation.
2.In this handler the name of file is hard-coded.But can I use some filter,like Regex “.txt$” to select all txt’s,and then select the first(or second) of them?It can be done by using Collection?

Frf says 1 December 2007 @ 05:47

get it.I simply swapped this two lines. Now it works.
But answer pls the rest questions

Lucy says 2 December 2007 @ 07:57

Hi Jeroen,
Thanks a lot for your answer on how to use ConfirmDialogHandler, it works.

Now I ran into another problem when I am doing Watin. One of my test page ( Page1) contains one Iframe, then Iframe has one link to another page(Page2) where has TextField field1 and Button btn1 within Iframe. But I am not able to catch the TextField and Button in Test page(page1).
From view source on page1, I only have .

So how can I catch and Find fields to type text, then catch the btn1 and click btn1 to go forward to next step? Thanks for your help in advance.

Kate says 18 January 2008 @ 04:52

Interesting validation test – out of curiosity, is there any way for Watir to relay any errors back in the results?

Wally says 8 May 2008 @ 06:33

Hi Lucy,

I ran into a similar problem (I think). For me, I was trying to access a TableCell containing the text “blah” that was inside an iframe called “contentFrame”.

This was not working:
TableCell cell = ie.TableCell(Find.ByText(“blah”));

Here is what I did to get it working:
Frame frame = Frame(Find.ById(“contentFrame”));
TableCell cell = frame.TableCell(Find.ByText(“blah”));

Appaji says 4 July 2008 @ 06:54

just tried following code
ConfirmDialogHandler confirmBox = new ConfirmDialogHandler();

using (new UseDialogOnce(ie.DialogWatcher, confirmBox))
{
ie.Link(Find.By(“id”, “ctl00_m_g_dffad0ec_5e72_4730_bc8a_c2abbd11e363_ctl00_ctl01_ctl00_toolBarTbl_RptControls_diidIODeleteItem_LinkText”)).Click();
// confirmBox.WaitUntilExists();
confirmBox.OKButton.Click();
}

it is not doing as expected, waiting for manual click on ok button.

anyone can suggest what is wrong in code

appaji

Greg Bray says 15 November 2008 @ 12:13

Here is code that I am using to accept a confim javascript dialog:

WatiN.Core.DialogHandlers.ConfirmDialogHandler cdhPopup = new WatiN.Core.DialogHandlers.ConfirmDialogHandler();
using (new WatiN.Core.DialogHandlers.UseDialogOnce(ie.DialogWatcher, cdhPopup)) //This will use the DialogHandler once and then remove it from the DialogWatcher
{
ie.Button(Find.ById(new Regex(“[a-zA-Z0-9_:]*” + btnUpdateID))).ClickNoWait(); //Find button using Regex to filter out extra ID elements from containers

cdhPopup.WaitUntilExists(5); //Wait max of 5 seconds for the confirm box to pop up
cdhPopup.OKButton.Click(); //Press OK
ie.WaitForComplete(); //Wait for page to finish loading
}

Tanvir says 25 November 2008 @ 05:25

there is useful link bellow about implementing different html and JS confirmation control in WatiN:
http://tanvirdotnet.blogspot.com/2008/11/ui-test-with-watin-for-tdd-in-net.html

Rakesh says 5 December 2008 @ 01:52

Has some one worked with multiple nested iFrames in IE with Watin….
If so plesae help us publishing a Post….
Since, I am new to watin ..it would be of great help

sathya says 19 February 2009 @ 07:41

I am using IE 8 and I am having hard time to click on a ‘OK’ button on alert dialog box here is the code i am usingn

IE ie = new IE(“linkxxxx”);
ie.TextField(Find.ByName(“txtUserName”)).TypeText(“xxxxx”) ;

AlertDialogHandler alertPopup = new AlertDialogHandler();
ie.AddDialogHandler(alertPopup);
using (new UseDialogOnce(ie.DialogWatcher, alertPopup))
{
ie.Link(Find.ById(“submitbtn”)).ClickNoWait();

alertPopup.WaitUntilExists();

alertPopup.OKButton.Click();
ie.WaitForComplete();
}

ie.RemoveDialogHandler(alertPopup);

Seeem not working any Idea on this

sunil says 14 April 2009 @ 01:04

Hi,
I need to write a script for following scenario:
1. Click on Browse button. Which will open the Browse for folder window
2. Select the file and Click on upload file.

Basically i need to upload on file by selecting file in browse for fole window.

Can any one guide on this.

Thanks
Sunil

Leave a Comment

Name (required)

E-mail (required - not published)

Website

Your comment: