dinsdag 9 december 2008

WatiN roadmap

Today I received a question on the mailing list about the status of WatiN 2.0. I thought I publish my response here as well.

No release date(s) yet, but I can report about the progress and speculate about a roadmap:

  • Currently the development code (a continuation of the 1.3 code base) is refactored to add support for testing with different browsers (IE, FireFox for now). I estimate I'm at 60% of the changes that need to be done to get the basic stuff to work.
  • Next is the integration of the firefox specific code from the 2.0 CTP version into the development code. I estimate that I will go for a first integration around the end of this month.

So my current guesstimate is that a beta or final release of 2.0 will be earliest at the end of q1 2009.

In the meantime Edward Wilde will update the current 2.0 CTP version to add support for FireFox 3.x. We haven't spoken about a release date be hopefully this refresh will be available early next year.

I will also release WatiN 1.3.1 in December with some important bug fixes.

So nothing final yet but I hope this gives you (all) a feel of what we are aiming for.

WatiN Find.ByDefault explained

My next post will be Part III of Supporting custom elements with WatiN. I got so excited about the possibilities that I started working on a wrapper for the Ra-Ajax controls as a show case. It is becoming very COOL and its fun to create as well. But as I said in my next post I'll show you more.

This post is all about a new feature of WatiN 1.3 which might not be that discoverable so I felt it needed some promotion.

Find.ByDefault

When you want to automate some action against an element on a web page, WatiN offers you different types of elements (TextField, Button, etc) to find an element. Each of these offer overloads accepting something to find the element by. For example, if you want to find a TextField by its id ("firstName_Id" in this example) you would typically have this code:

var firstNameField = ie.TextField("firstName_Id");

Which is the same as:

var firstNameField = ie.TextField(Find.ByDefault("firstName_Id"));

And with the default Settings applied, it is the same as:

var firstNameField = ie.TextField(Find.ById("firstName_Id"));

In this post I'll show you how you can change the implementation and behavior of Find.ByDefault() by creating your own default factory class and assigning it to Settings.DefaultFinderFactory .

Make Find.ByName the default

If your site makes heavy use of the name attribute on elements (instead of ids) and you use these to find the elements in your tests, you would typically write code like:

var firstNameField = ie.TextField(Find.ByName("firstName_Name"));

Lets make Find.ByName() the default so your code will look like this:

var firstNameField = ie.TextField("firstName_Name");

To make this work we need to create a new class which implements IFindByDefaultFactory. In the implementation of the ByDefault(string) method we can simply return Find.ByName(value). This is the implementation of the class:

public class FindByNameFactory : IFindByDefaultFactory
{
public BaseConstraint ByDefault(string value)
{
  return Find.ByName(value);
}
    public BaseConstraint ByDefault(Regex value)
{
  return Find.ByName(value);
}
}

The last thing we need to do is register this new factory class and Find.ByName will be the new default:

Settings.FindByDefaultFactory = new FindByNameFactory();

Handle ASP ids with

Another very useful way we could leverage this new feature is dealing with ASP.Net ids. When you automate a website which is build with ASP.Net webforms you probably have to deal with very long ids. By default ASP.Net creates unique ids by concatenating all of the ids of the container controls a control is in. For instance if a TextBox to enter a first name is placed within a tab control container and this is placed in a placeholder, the resulting id could be something like "plc_Main$tab_Details$txt_FirstName". With WatiN you have several ways to find this TextField by id.

Option 1: Using the full Id
The simplest option to find the control is to use the full id. Example:

ie.TextField("plc_Main$tab_Details$txt_FirstName").TypeText("Jeroen");

There are two disadvantages to this approach:
- It relies heavily on the ids of the controls the ASP:TextBox control is placed in. If you remove/rename or add another container to the stack, the TextField won't be found cause the id is changed.
- The longer the id gets the less readable this line of code becomes. Although you can (should in my opinion) abstract the way you find your controls by using a Page model, this still is massy.

Option 2: Using a regular expression
The second option is to find the control by using the overload on TextField which accepts a regular expression. In the following example we create a Regex which matches TextFields ending (hence the $ sign) with "txt_FirstName" :

ie.TextField(new Regex("txt_FirstName$")).TypeText("Jeroen");

Option 3: Make WatiN handle ASP ids by itself

public class FindByAspIdFactory : IFindByDefaultFactory
{
public BaseConstraint ByDefault(string value)
{
  return Find.ById(new Regex(value + "$"));
}
    public BaseConstraint ByDefault(Regex value)
{
  return Find.ById(value);
}
}

Register this new factory class:

Settings.FindByDefaultFactory = new FindByAspIdFactory();

And you can find controls with ASP ids like:

ie.TextField("txt_FirstName").TypeText("Jeroen");

Using (magic) prefixes

If you don't like to always use a regular expression, like we did in the previous example, you could implement something like the Incisif automation framework offers. They choose to put a prefix in front of the ASP id. So only if the specific prefix (a:) is in the id a lookup with a Regex will be done to find the element. Example:

ie.TextField("a:txt_FirstName").TypeText

and the Factory implementation:

public class FindByAspIdFactory : IFindByDefaultFactory
{

public BaseConstraint ByDefault(string value)
{
  if (value.StartsWith("a:")
  {
   Regex regex = new Regex(string.format("(.*\${0}$)|(.*_{0}$)", value));
   return Find.ById(regex);
  }
  return Find.ById(value);


public BaseConstraint ByDefault(Regex value)
{
  return Find.ById(value);
}
}

Other possible uses

Put the attribute you want to match with in front of the value

ie.TextField("id:txt_FirstName").TypeText
ie.TextField("name:txt_FirstName").TypeText

A ruby like notation to express a regular expression (starting and ending with a forward slash) instead of having new Regex() all over your code:

ie.TextField("/txt_FirstName$/").TypeText

And I'm sure you can come up with many more possible uses.

Enjoy!

woensdag 12 november 2008

Supporting custom elements with WatiN, Part II

12 Nov 2008: Updated section about creating an extension method.

In part I of this series I explained why you should create wrappers and how you could create a basic wrapper for your custom or third party controls. In this post I will show you

  • how to make use of the deferred find execution
  • how to use extension methods to make it all look integrated with WatiN

What is Deferred Find Execution

Deferred Find Execution in WatiN means that the search for an element in the current web page is done the first time you get a property value, set a property value or call a method on that element. An example:

using(IE ie = new IE())
{
TextField name = ie.TextField("name");

ie.GoTo(new Uri(Util.HtmlTestBaseURI, "EnterYourName.htm"));
name.TypeText("Jeroen");
}

The first line will open a new Internet Explorer instance showing you an empty page (about:blank). Next we set the name variable to point to an input element of type text with the id 'name'. If WatiN did an immediate lookup it would throw an ElementNotFound exception cause these elements aren't on the page shown in the browser. But WatiN doesn't throw an exception, it actually returns an instance of TextField for which the find isn't executed yet. Then the browser navigates to a page with this name element on it. When calling TypeText on the TextField instance, WatiN will search for (and find) the input element with id 'name' before executing the TypeText action. So finding of the name element is deferred until the code actually needs a reference to that element.

WatiN heavily relies on this behavior so it seems like a good idea to play with the rules.

Implementing deferred find execution

To make this work for the DateTimeElement an element finder class needs to be passed to the base class of DateTimeElement. Lets replace the current constructor, which accepts a Table instance, with one that calls the base constructor accepting an INativeElementFinder instance. For readability I created a private method CreateElementFinder which does the actual creation of the INativeElementFinder instance. The base constructor also needs a DomContainer instance which can be provided by our IE instance in our test code. Here is the reviced code for DateTimeElement:

public class DateTimeElement : Table
{
public DateTimeElement(DomContainer domContainer, string id) :
base(domContainer, CreateElementFinder(domContainer, id)) { }

public void TypeDateTime(DateTime value)
{
TextField(new Regex("^date$")).TypeText(value.ToString("dd-MM-yyyy"));
TextField(new Regex("^time$")).TypeText(value.ToString("hh:mm:ss"));
}

private static INativeElementFinder CreateElementFinder(DomContainer domContainer,
string id)
{
return domContainer.NativeBrowser.CreateElementFinder(ElementTags,
Find.ById(id),
domContainer);
}
}

And here is how the code in our test looks like:

DateTimeElement element = new DateTimeElement(ie, "datetime2");
element.TypeDateTime(new DateTime(2008, 11, 1, 12, 00, 00));

All in all a small code change for a big change in behavior.

Introducing CustomElementFinderHelper

In the above code I choose to create the ElementFinder instance inside the DateTimeElement class. I did this because my focus was on introducing deferred find execution (always make small steps). But if we are going to create more wrappers for our custom or third party controls, this CreateElementFinder method seems to be needed in all of them. So lets do some refactoring and move this method into a new class which will be passed into the constructor of DateTimeElement. Here is the new class CustomElementFinderHelper:

public class CustomElementFinderHelper
{
private readonly DomContainer _domContainer;
private readonly BaseConstraint _constraint;

public CustomElementFinderHelper(DomContainer domContainer,
BaseConstraint constraint)
{
_domContainer = domContainer;
_constraint = constraint;
}

public DomContainer DomContainer
{
get { return _domContainer; }
}

public INativeElementFinder CreateElementFinder(ArrayList supportedTags)
{
return _domContainer.NativeBrowser.CreateElementFinder(supportedTags,
_constraint,
_domContainer);
}
}

And this is our cleaned up DateTimeElement code:

public class DateTimeElement : Table
{
public DateTimeElement(CustomElementFinderHelper helper) :
base(helper.DomContainer, helper.CreateElementFinder(ElementTags)) { }

public void TypeDateTime(DateTime value)
{
TextField(new Regex("^date$")).TypeText(value.ToString("dd-MM-yyyy"));
TextField(new Regex("^time$")).TypeText(value.ToString("hh:mm:ss"));
}
}

And to top things of, the extension method

Our last refactoring has turned our testing code into an unreadable mess:

CustomElementFinderHelper helper = new CustomElementFinderHelper(ie, Find.ById
("datetime2"));
DateTimeElement element = new DateTimeElement(helper);
element.TypeDateTime(new DateTime(2008, 11, 1, 12, 00, 00));

The intention is completely lost and there is stuff we need to do over and over again if we use a DateTimeElement in our test automation. Again refactoring to a method will help, but wouldn't it be nice if we could use the same syntax in our tests as we use to find for instance a TextField. So we need to extend the IE class in some way.

One option would be to create a new sub class of the IE class, add a new method to find the DateTimeElement and use the sub class in all our tests.

A new and simpeler solution to get the same intellisense support and readable code in your tests, is the use of an extension method. We do have several options for our extension. We could use the IE class to extend. But a better candidate would be the DomContainer (inheritted by IE) so that this extension method is also available on the HtmlDialog class (for html popup dialogs). With our current implementation of CustomElementFinderHelper extending DomContainer seems to be the logical choice. So lets create a static DateTimeElementExtensions class with an extension method on DomContainer:

public static DateTimeElement DateTimeElement(this DomContainer domContainer,
string id)
{
CustomElementFinderHelper helper = new CustomElementFinderHelper(domContainer,
Find.ById(id));
return new DateTimeElement(helper);
}

And thanks to the magic of extension methods the code in our test now looks like:

ie.DateTimeElement("datetime2").TypeDateTime(new DateTime(2008, 11, 1, 12, 00, 00));

This refactoring cleans up the code big time and makes it look like DateTimeElement is part of the WatiN API itself. Ain't that sweat.... :-)

If your still using C# 2.0, first go to your manager and (again) explain why you need to move to C#3.0 (and it's a breeze!). If the answer (again) is NO than remove the this keyword from the method signature and you can call it like this:

DateTimeElementExtensions.DateTimeElement(ie, "datetime2").TypeDateTime(new
DateTime(2008, 11, 1, 12, 00,00));

Almost there

And now you want to access a DateTimeElement as a child from a Frame, Div or Table element..... That is a no go with the current solution.

In part III I will show you how we can fix this by making some changes to the CustomElementFinderHelper and again the magic of extension methods. But this time on one of WatiN's interfaces.

vrijdag 7 november 2008

Supporting custom elements with WatiN, part I

When using WatiN it is easy to automate simple actions on standard elements in the browser. But when you try to automate (complex) controls of control vendors like Telerik, Infragistics or even your own custom controls, things get to start ugly quickly. Wouldn't it be nice if we could automate these controls in the same way we do automate a TextField ?

In this blog post and the next one, I while show you how.

The html code to automate

Lets start small and simple. The following HTML snippet is created by a (fictitious) custom date time control. For one of our (again fictitious) tests we need to set the values of the date and time input elements.

<table id="datetime2">
    <tr>
        <td>
            <input type="hidden" id="original_date" value="01-01-2007" />
            <input type="text" id="date" value="01-01-2007" />
        </td>
        <td>
            <input type="hidden" id="original_time" value="10:00:00" />
            <input type="text" id="time" value="10:00:00" />
        </td>
    </tr>
</table>

As you can see the html for this date time control contains two input fields, one for the date and one for the time. There are also two hidden input elements containing the original values of the input fields.

The simple way

To automate setting the date and time the following code will do the job nicely.

IE ie = new IE("http://www.examples.watin.net/datetime.html");

ie.Table("datetime2").TextField(new Regex("^date$")).TypeText("01-11-2008");
ie.Table("datetime2").TextField(new Regex("^time$")).TypeText("12:00:00");

So what's wrong with this code you might wonder. It basically comes down to being code and logic that isn't reusable. To name two issues:

  • To get the correct TextField for the date or time, you need to remember (or find out the hard way) the correct regular expression. Otherwise you might end up setting the value of the hidden input element which will give you a nice exception (and then you remember... and I can tell).
  • The formatting of the date and time needs to be in this specific format for the control to work.

We could refactor this into a method, but still. WatiN offers you all kinds of objects that wrap html elements so it feels more natural for this date time control to be wrapped as well.

Basic wrapping

So lets create a wrapper. I will use Table as the base for our new element class since Table seems to be the container element for the control.

public class DateTimeElement : Table
{
    public DateTimeElement(Table table) : base(table) { }

    public void TypeDateTime(DateTime value)
    {
        TextField(new Regex("^date$")).TypeText(value.ToString("dd-MM-yyyy"));
        TextField(new Regex("^time$")).TypeText(value.ToString("hh:mm:ss"));
    }
}

We can use this new DateTimeElement class in our tests like this:

DateTimeElement element = new DateTimeElement(ie.Table("datetime2"));
element.TypeDateTime(new DateTime(2008, 11, 1, 12, 00, 00));

In my opinion the readability and intention of this code is way better then our first approach. And it also encapsulate the knowledge of the regular expression and the specific formatting of the date and time.

So are we happy now. Not completely. I think the syntax of creating the new DateTimeElement can be improved cause it feels weird to pass in a table element instance into the constructor. Another, not visible, drawback of passing in an element is that it doesn't use WatiN's deferred execution to find the table element on the page. In more complicated wrappers this might lead to unexpected behavior. For now I will spare you the details.

Stay tuned

In part II I will show you how you can get deferred find execution by providing a small helper class and making some changes to the DateTimeElement constructor.

To top things off I will use extension methods (C# 3.0 only) to give you this end result in you test code:

ie.DateTimeElement("datetime2").TypeDateTime(new DateTime(2008, 11, 1, 12, 00, 00));

Stay tuned....

Tags van Technorati:

vrijdag 31 oktober 2008

Using HttpWatch with WatiN

Combine WatiN and HttpWatch (free edition) and keep track of the performance statistics of your web site. I think it is a cool idea and will try this out for sure. Following a link with example code on how to combine both API's.

http://blog.httpwatch.com/2008/10/30/using-httpwatch-with-watin/

woensdag 15 oktober 2008

Released WatiN 1.3!

I'm happy to announce the release of WatiN 1.3!

In this release support for lamdba expressions and Linq is added (for the .Net 2.0 version and higher). New helper methods are added to the Find class, two new DailogHandlers have been added and many many other changes.

This is the last 1.X release (so no FireFox support in this release) and will also be the last release supporting .Net 1.1.

Many thanks to all who contributed to this release!

For the full release notes visit http://watin.sourceforge.net/releasenotes-1-3-0-4000.html

vrijdag 12 september 2008

Extend WatiN with Extension methods

Just a quick pointer to an article by Richard Allen. He is showing how easy it is to extend WatiN with extension methods and gives you some extensions he found valuable to use. Have a look at http://richallen.blogspot.com/2008/08/watin-ajax-and-some-extension-methods.html.

I've done some nice stuff with extension methods as well to add support for your own controls (or third party controls) "to" the WatiN API. My blog post about it is still in draft version but will be posted soon.

Another great blog post by Richard is about writing a framework to wrap WatiN and make your (functional) test code more fluent and domain specific. He calls it the WatiN Controller Framework but is looking for an other name (what about WaCoFra, kidding). For does who are interested, follow this link http://richallen.blogspot.com/2008/08/watin-controller-framework-part-i.html

donderdag 24 juli 2008

WatiN in WatirPodcast episode 4

Last week I did a Skype interview with Zeljko Filipin the host of WatirPodcast. Episode 4 is all about WatiN, how it compares to Watir, when and why I started the project, who should use WatiN and many more things are discussed. A big thanks to Zeljko for the invitation.

So go check it out at http://watirpodcast.com/4/. I hope you like it.

donderdag 17 juli 2008

WatiN and Vista tip

Since the release of Vista people experienced (and report) problems with running WatiN test on a Vista machine. A common way to get it working was to turn of the new User Account Control (UAC) feature in Vista.

James Kovacs found a (lot) easier way to make your tests work and still have UAC enabled: Just add the domain of the web site under test to the trusted sites list of Internet Explorer. This will turn Protected mode off for this site and your WatiN tests will run properly. Thanks James for sharing this info!

For more info visit http://www.jameskovacs.com/blog/RunningWatiNTestsOnVista.aspx

woensdag 21 mei 2008

DevDays 2008

Coming Thursday and Friday I'll be at the DevDays in Amsterdam (just listening not talking). If you're there and would like to meet, post a comment and we can arrange something.

woensdag 23 april 2008

WebUnitAsp 2.0

If you like the way NUnitAsp handled things this might be one for you. WebUnitAsp (http://dotnet.org.za/colin/archive/2008/04/22/webunitasp-2-an-experiment-in-unit-testing-asp-net.aspx) is a wrapper around the WatiN API and gives you the look and feel of using NUnitAsp. This version of WebUnitAsp is based on the WatiN 2.0 CTP.

Tags van Technorati:

woensdag 9 april 2008

Make TableCell handle TH elements as well

Update 9/18/2009: The described solution described below no longer works as of WatiN 2.0 beta 1. See this blog post for the new way.

Original blog text:

Going through the WatiN bug tracker on SourceForge I came along this bug report:

"When you retrieve a Table Row that contains TH elements, the TableCells collection returns empty. The reason being TableCell only recognizes TD as a valid tagname, when it should recognize TH."

I agree that TH elements probably should be seen as a TableCell as well. I'm a bit reluctant though to fix this because this will change the behavior of WatiN possibly resulting in unexpected failing tests for users of WatiN. So I did a small experiment to see if you (as a user of the WatiN API) are able to make WatiN handle TH elements as a TableCell if you need it to.

First I created this HTML table for my test

image 

Next I wrote and ran the following unit test and..... it passed!

image

The trick is in the line:

TableCell.ElementTags.Add(new ElementTag("th"));

This registers the TH element as an element that can be wrapped by the TableCell class.

I tested this against the current development code and it run just fine. It probably will work as well with the current 1.2.1 and 2.0 CTP releases.

Technorati Tags:

donderdag 3 april 2008

WatiN 1.3 adds support for .Net 3.5

I'm finishing of the last bits before the release of WatiN 3.5. Recently I have added support for .Net 3.5 specific features. Following is a screenshot of some of the tests. As you can see:

  • WatiN will have support for lambda expressions and I think this offers an exciting new syntax!! See the first test in the screenshot.
  • WatiN will have support for Linq and the Linq Extension methods. See the last two tests.

I have done some speed tests on both and performance is just as good as when using the "old" Find.By... syntax which uses AttributeConstraints and Comparers internally.

I'm finishing the changes to use lambda's this week. The changes to support Linq are already committed to the SVN repository on SourceForge so you can check that out if you can't wait until the release of 1.3, which won't be long btw.

image

Tags van Technorati:

woensdag 2 april 2008

Catching server errors with WatiN

One of the main benefits of testing through the web browser is that you are testing everything related to the creation and rendering of the HTML. Even if you use the Model-View-Presenter or Model-View-Controller patterns and have unit tests for the model and the presenter(s), the view is still hard to test without firing up the web browser and seeing the actual page. At my daily work we use the MVP pattern with .Net ASPX pages so we can test most of the UI logic without having to fire up a browser. Still there is a need to test the UI using WatiN cause there are things which can't be tested in any other way.

Occasionally tests fail with an ElementNotFound exception. When investigating the problem the problem had nothing to do with a misspelled element id or something alike, but it was caused because of a "Server Error". The well know ASPX Server Error page was shown, giving you the exception message and the stack trace. The ElementNotFound was thrown because WatiN was looking for the element on this (unexpected) Server Error page.

image

Wouldn't it be nice if we would have a more accurate exception instead of the ElementNotFoundException and also have all the information shown on the Error page available after the test has failed? You can do this with WatiN and I will show you how.

First we need to create a class which inherits from the WatiN.Core.IE class, let's call our class MyIE for this example. We will use this class in our tests instead of the IE class.

Now that we have this class in place, we need to add a hook to check if the "Server error" page is shown. A great place to add this is the WaitForComplete method cause every action executed by WatiN on your page which could lead to a postback to the server will call this method. So the GoTo(url), the Click() on a button or the TypeText() on a input element (and many more actions) all call the WaitForComplete method to make sure any further action is postponed until a possible postback has returned its result.

Knowing this, we can override the WaitForComplete method, call base.WaitForComplete and than check for the text "Server error" on the page. Here is the code:

image

This is an example of a test using the MyIE class.

image

And here is how a failing test result due to a "Server Error" looks in ReSharper:

image

Conclusion

Creating your own IE sub class gives you the possibility to improve the information you get from a failing test due to a Server Error. In this case the ServerErrorException gives you:

  • A clear indication of the real problem instead of the more verbose ElementNotFoundException stating that it couln't find an element on the page.
  • Information about the "Server Error"
  • Early failure of the test  because the ServerErrorException gets thrown even before WatiN start looking for the element which isn't there and finally times out.

Using this pattern of inheriting IE in your own class gives you many more options. In one of my next blogs I will show you how you can utilize this to tweak the performance of WatiN.

Tags van Technorati:

woensdag 12 maart 2008

Released WatiN 2.0 CTP

Update: These code samples will no longer work as off WatiN 2.0 beta 1 and higher

I just released WatiN 2.0 Community Technology Preview! So start automation IE and/or FireFox with WatiN as of today. Read how to get started and download the release here. Be aware that the API can (and there for will) change till the final 2.0 release. Here is an example:

[Test]
public void SearchForWatiNOnGoogleVerbose()
{
    using (IBrowser ie = BrowserFactory.Create(BrowserType.InternetExplorer))
    {
        ie.GoTo(http://www.google.com);
        ie.TextField(Find.ByName("q")).Value = "WatiN";
        ie.Button(Find.ByName("btnG")).Click();
        Assert.IsTrue(ie.ContainsText("WatiN"));
    }


    using (IBrowser firefox = BrowserFactory.Create(BrowserType.FireFox))
    {
        firefox.GoTo(http://www.google.com);
        firefox.TextField(Find.ByName("q")).Value = "WatiN";
        firefox.Button(Find.ByName("btnG")).Click();
        Assert.IsTrue(firefox.ContainsText("WatiN"));
    }
}

A big thanks to Edward Wilde for all the work he has done on integrating FireFox in WatiN! And of course we like to hear what you think of it.

Have fun

Tags van Technorati:

woensdag 5 maart 2008

Agile development and WatiN

Because I'm interested in what people say about WatiN I look around at Technorati (http://technorati.com) from time to time. From there I picked up this great article about Agile development and WatiN. I think Rainer and Karin has put to gather a very nice article! Thanks guys! Have a look at how they use Visual Studio 2008, Linq-To-Sql and WatiN at http://www.software-architects.com/TechnicalArticles/TestDrivenDevelopment/tabid/83/language/en-US/Default.aspx

Strange thing is that I implemented somewhat in the same way our example application for our presentation at Agile Egypt. I don't have any article going with it but if you are interested you can download the example code (http://www.agileegypt.com/presentations_March2008/Agile%20Lab%20code.zip) and our presentation (http://www.agileegypt.com/presentations_March2008/Agile%20Lab.pdf) from the agile egypt site (www.AgileEgypt.com).

The purpose of our presentation was to show people a small agile development project using Test Driven/First Development. So we went through the creation of a web site to sign up for a course:

Iteration 0: Setting up source control and continues integration

Iteration 1: A very basic implementation of a list of courses

Iteration 2: TDDing our way through our tasks for creating a sign up page for a course.

In our presentation we used :

  • Visual Studio 2008 with Linq. I love the new Linq syntax and the extensions methods it adds to types implementing IEnumerable.
  • ASP.Net MVC Toolkit (http://www.asp.net/downloads/3.5-extensions/). After playing with it a bit I knew immediately that I didn't want to  go back developing in webforms..... But reality is different.
  • WatiN (http://watin.sourceforge.net). Need I say more :-)
  • NUnit 2.4 (http://www.nunit.org/). Because we are familiar with it and (still) like it. Especially the Assert.That() syntax is sweet.
  • LightORM (http://www.codeproject.com/KB/database/LightORMLibrary.aspx), an open source API to persists your classes to a database. We choose this because we wanted to show the audience that it is possible to start without having to setup a database before you start coding, the Test Driven Development way of doing things (yes we went very pure on this).
  • NAnt (http://nant.sourceforge.net/). Mainly because we aren't familiar with MS Build and NAnt has served us great in the past and probably will in the future.
  • Hudson (https://hudson.dev.java.net/) for continues integration. It is a JAVA application and really simple to setup for continues integration. Just kick of the build.bat file from Hudson, which will kick of the NAnt build and you have your CI running.
  • And last but not least we used SubVersion (http://subversion.tigris.org/) and TortoiseSVN (http://tortoisesvn.tigris.org/) as our source control system.

Have a look at the code and let my know what you think of it. And don't forget to install the ASP.NET MVC Toolkit release from last December before firing up the code in Visual Studio. There is also a build.bat file to kick off the (automated) build.

Tags van Technorati:

woensdag 20 februari 2008

Remote pair programming

In preparation for Agile Egypt (www.agileegypt.com) my pair and I are creating a presentation and an example application. Since we have our try out tomorrow evening we wanted to go through the code examples some more. Because of schedules we couldn't meet in person so I proposed a remote pair programming session.

In one word: Amazing! And simple to set up as well.

We downloaded RealVNC (www.realvnc.com) for sharing a desktop. Installation and use of RealVNC is smooth. Don't forget to do some port forwarding in your internet router/firewall.

After installation simply start the realvnc server and publish your ip address to your pair. He will have to start the realvnc client, enter the ip address, press connect and its code time!

For the communication we used Skype (www.skype.com) to talk to one another. Even with realvnc sending the highest level of color depth over the wire (modest cable connections on both sides) there were no band width issues at all. I could hear my pair typ in Visual Studio 2008 and see his changes instantly.

Another positive thing about this way of pairing: No need to change chairs or keep passing keyboard and mouse around, we both had a keyboard and mouse.

Using this setup we had a very productive evening and it left me wondering "Who needs an office these days?".

 

Tags van Technorati:

zaterdag 16 februari 2008

ReSharper 4.0 Early Access Release

I'm in the process of installing all my development software on my (new) laptop. When I installed ReSharper and took a look at there website, I noticed the first early access release is available of version 4.0. This can be used with VS 2005 and VS 2008. You can download the setup and checkout the ReSharper 4.0 EAP Notes for all the new goodness here:

http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+Early+Access+Program

YES!!

dinsdag 12 februari 2008

New blog layout

Today I went searching for a template for this blog that better matched the style of the WatiN website. After a bit of looking around I found this one and I like it.

A thank you to "Free SCC Templates" and "Blog and Web" (scroll to the bottom for their links, and no they won't show up in your RSS reader :-) ).

donderdag 7 februari 2008

Will Silverlight 2.0 become automation friendly?

With the current Silverlight releases available (1.0 and 1.1 aka 2.0) there is no way to automate the user interface. No support for Active Accessibility or UI Automation. But when signing up for the Beta of the Microsoft Download center, which uses Silverlight, I noticed this:

image

In my opinion this looks like the final release of Silverlight 2.0 will have Active Accessibility and/or UI Automation in place. This opens up the way for test automation. And I think that is a good thing!

woensdag 6 februari 2008

How to create tutorial videos with silverlight

I'm thinking about doing tutorial videos about WatiN. I have been experimenting with Camtasia and that is a great product. But now that I have a video where will I host it?

And then this article on Code Project came along http://www.codeproject.com/KB/scrapbook/HowToCreateArticleVideos.aspx. This is going to help me a lot. Thanks Karl for posting this detailed info and thanks Microsoft for the free hosting of silverlight videos.

And now back to take four, five, six (I lost count) of the "Getting started with WatiN" video.....

donderdag 31 januari 2008

Using WatiN to test select lists in an update panel

UPDATE:  This looks like another great way to handle automating selectlists in AJAX scenarios http://pushpontech.blogspot.com/2008/04/ajax-issues-with-watin.html

In our web application we make use of update panels to give the user a nicer UI experience. On one of our web pages a SELECT element contains a list of countries. When the user selects a country from this list, a post back occurs and another SELECT element is populated with states for the selected country. A very basic scenario.

When automating this scenario with WatiN the following test would pass if the select elements are not wrapped in an update panel.

[Test]
public void ShouldOnlySeeStatesFromSelectedCountry()
{
// Open the web page with country and state selection lists
IE ie = new IE("http://localhost/dropdownwithwatintoken/MyDetailsPage.aspx");

// Select a country from the list
ie.SelectList("countries").Option("Netherlands").Select();

// Do the asserts
Assert.That(ie.SelectList("states").Options.Length, Is.EqualTo(12));
Assert.That(ie.SelectList("states").Option("Utrecht").Exists);
}


But after the countries and states lists were wrapped in an update panel, this test started to fail. WatiN doesn't recognize an AJAX call so it will not wait until it is finished. So what happens: After the select is made an async postback is initiated. Then the next line in the test is executed but this is before the response from the server is received and rendered. So no values are in the states list and the test will fail.



Although each element in WatiN has a WaitUntil(attributeconstraint) method, for this test we couldn't figure out what to wait for in the UI to know for sure the async postback was finished (received and rendered). So we decided to add a Thread.Sleep(someSeconds) for the time being. Although this worked, the test was waiting longer than necessary. If there only was an attribute on the SELECT element that would change after the async postback.... Using the WaitUntil method, we could wait for the change of the attribute value and then proceed.



The solution was to create a new custom web control that inherits from DropDownList and overrides the AddAttributesToRender method.



public class DropDownWithWatiNToken : System.Web.UI.WebControls.DropDownList
{
protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
{
base.AddAttributesToRender (writer);
if (AutoPostBack)
{
writer.AddAttribute("watintoken", DateTime.Now.Ticks.ToString());
}
}
}


Each time the HTML for this control gets rendered an attribute "watintoken" will be added with the current date and time represented in ticks. So now we have the needed attribute to check for!



To use this we need to replace our asp:DropDownList usages in our web page(s). To do so you should register this custom control in your aspx web page in which you are going to use the DropDownWithWatiNToken control.



<%@ Register TagPrefix="watin" Namespace="CustomWebControls" Assembly="DropDownWithWatiNToken" %>


Then replace asp:DropDownList with watin:DropDownWithWatiNToken. It should look something like this:



<watin:DropDownWithWatiNToken ID="countries" Runat="server" AutoPostBack="True" />


Now that we have changed the controls on the web page, we can use the "watintoken" attribute in our test and wait until it is changed. After the attribute value has changed, we can proceed the test.



[Test]
public void ShouldOnlySeeStatesFromSelectedCountry()
{
// Open the web page with country and state selection lists
IE ie = new IE("http://localhost/dropdownwithwatintoken/MyDetailsPage.aspx");

// Remember the value of the watintoken attribute
string watintoken = ie.SelectList("countries").GetAttributeValue("watintoken");
// Select a country from the list
ie.SelectList("countries").Option("Netherlands").Select();
// Wait until the value of the watintoken attribute is changed
ie.SelectList("countries").WaitUntil(!Find.By("watintoken",watintoken));

// Do the asserts
Assert.That(ie.SelectList("states").Options.Length, Is.EqualTo(12));
Assert.That(ie.SelectList("states").Option("Utrecht").Exists);
}


And all tests are green again!



Tags van Technorati: