Introduction
Nowadays, testing code is not so difficult. We have lots of tools that help us to achieve this goal. But in Web application development, we use to forget to test one of the most important layers, the UI. In this article, I'm going to briefly cover how to do UI and functional testing using WatiN (Web Application Testing In .NET). I'll try to show you what impressed me of this framework from a developer point of view.
What is WatiN?
WatiN stands for Web Application Testing In .NET, and it's a framework that enables web application testing through Internet Explorer. WatiN is inspired on WatiR, a web application testing framework for Ruby. The way that WatiN works is very "easy", knowing that there's a lot of work behind a framework like this. WatiN lets you open Internet Explorer instances, and through interop, interact with the elements in a form. With WatiN, you can get and set values from the elements in a form, and you can fire events of any of the elements in the document too.
Getting started
The API of WatiN is very user-friendly, and watching some code examples and digging a bit, you can get the benefit of this framework quickly. The first thing that we need to build a test method using WatiN is to add a reference to WatiN.Core.dll. WatiN gives us four namespaces; the one that we use to interact with IE instances is WatiN.Core
. The different namespaces available are:
WatiN.Core.DialogHandlers
: The namespace that provides the needed objects to manage the dialogs that the browser can show to the user. Some of the handlers you can find in this namespace are, for example: AlertDialogHandler
, ConfirmDialogHandler
, FileUploadDialogHandler
, PrintDialogHandler
, and LogonDialogHandler
.
WatiN.Core.Exceptions
: The namespace that hosts specific exceptions that would help us to control any unwanted behavior. Some examples of these exceptions are: ElementNotFoundException
, IENotFoundException
, TimeoutException
, and a generic WatiNException
.
WatiN.Core.Interfaces
: The namespace that provides us some interfaces to extend the WatiN framework as, for example, the ILogWriter
interface that lets us implement our own LogWriter
class.
WatiN.Core.Logging
: The namespace that hosts classes that will help us to log the actions that our code is doing. We can use the DebugLogWriter
class to write log messages to the debug window, or we can use the Logger
class to save our logs in other ways.
Because the scope of this article is introductory, I will only show you the usage of the most important objects of the framework. In order to learn more about the rest of the namespaces and/or objects, you can check the "References & Resources" section at the bottom of this article.
Now that we have a brief idea of what WatiN is and what we can do with it, let's write some code to see the API's usage.
using WatiN.Core;
[STAThread]
static void Main(string[] args)
{
IE ie = new IE();
ie.GoTo("http://www.google.com");
ie.TextField(Find.ByName("q")).TypeText("WatiN");
ie.Button(Find.ByValue("Google Search")).Click();
}
The above code example opens an Internet Explorer instance and points it to www.google.com, then, when the page is loaded, it searches a textbox with name "q" and fills it with the text "WatiN". Then, we do the same we did for the textbox but, this time, looking for a button whose value is "Google Search". In this case, we perform the "Click()
" event of the button. If we run this console application, we will see an Internet Explorer opening and doing the things we coded. Isn't that cool?
Solving initial workarounds
Back into the previous piece of code, I have to mention a couple of things. In order to automate tasks with Internet Explorer, we need to run our test under a thread that has a single-threaded apartment (STA) as the apartment state. Notice that over the Main()
method, we have the [STAThread]
attribute that sets the apartment state to STA. A big question that I made myself when I came across this framework was, "And what about running this tests with NUnit?". NUnit doesn't run tests in a thread using the STA apartment state; it uses multithreaded apartment (MTA), but this isn't a big problem, so we can attach a config file to the NUnit test project and specify the apartment state mode that we want. Let's see how to do it:
="1.0" ="utf-8"
<configuration>
<configSections>
<sectionGroup name="NUnit">
<section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
-->
<add key="ApartmentState" value="STA" />
</TestRunner>
</NUnit>
</configuration>
If we run the "Getting started" code sample, Internet Explorer will remain open after the actions we coded are performed. This will not affect us if we only want to run one test; we only need to close one Internet Explorer window. But if we want to run more than one test, we can do two things to avoid having to manually close the Internet Explorer windows. First, we can create the instance of the IE object using the using
keyword and using the constructor that lets us specify the URL where the new instance will be pointed. If we do that, it gets closed and disposed after the test execution. Second, we can call the Close()
method of the IE instance to programmatically close the window. Both are valid, but I prefer the first one because I think that the resulting code is more elegant than if we used the second one.
Creating our first UI test
If our test result is based on the response that the server sends back to the browser, we can parse the response in order to know if our test has failed or not. The way that we do that is exactly the same that we use to make the test, but searching other items in the page that help us to identify what happened. Suppose that we have an aspx page like this:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" >
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>WatiN Test Page</title>
</head>
<body>
<form id="form1" runat="server">
<h2>Check if a nickname is already used</h2>
<table>
<tr>
<td>Nickname:</td>
<td><asp:TextBox runat="server" ID="txtNickName" /></td>
</tr>
<tr>
<td colspan="2" align="right">
<asp:Button runat="server" ID="btnCheck"
Text="Check !!" OnClick="btnCheck_Click" />
</td>
</tr>
<tr>
<td colspan="2">
<asp:Label runat="server" ID="lblResult" />
</td>
</tr>
</table>
</form>
</body>
</html>
And now, suppose that we have this code on the btnCheck.OnClick
event:
protected void btnCheck_Click(object sender, EventArgs e)
{
if (txtNickName.Text != "gsus")
lblResult.Text = "The nickname is already in use";
else
lblResult.Text = "The nickname is not used";
}
For writing these article code samples, I'm using Visual Studio 2005 running under Windows Vista, so I don't have Internet Information Services installed. For web development, I'm using the ASP.NET Development Server that came with .NET Framework 2.0, so in the next code sample, you will see that I start the Web Server programmatically in order to run the test. I think that this is a good way to decouple our UI tests from IIS. Now, let's see how the test looks finally:
using System.Diagnostics;
using WatiN.Core;
using NUnit.Framework;
using System;
namespace WebAppUITesting
{
[TestFixture]
public class UITesting
{
private Process p;
[TestFixtureSetUpAttribute]
public void SetUp()
{
p = new Process();
string path =
Environment.CurrentDirectory.Replace(@"WebAppUITesting\bin",
string.Empty);
p.StartInfo.FileName = "WebDev.WebServer.EXE";
p.StartInfo.Arguments =
String.Format("/port:8080 /path:\"{0}WebApp\"", path);
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.Start();
}
[Test]
public void CheckIfNicknameIsNotUsed()
{
using (IE ie = new IE("http://localhost:8080/Default.aspx"))
{
ie.ShowWindow(NativeMethods.WindowShowStyle.Maximize);
ie.TextField(Find.ById("txtNickName")).TypeText("gsus");
ie.Button(Find.ById("btnCheck")).Click();
Assert.AreEqual(true, ie.ContainsText("The nickname is not used"));
}
}
[TestFixtureTearDownAttribute]
public void TearDown()
{
p.Kill();
}
}
}
As you can see, I use the method ContainsText
in line 46. This method searches the specified text in the document content and returns a bool
indicating if there are occurrences in the search. We can replace line 46 with the next code example, where we directly get the "lblResult
" Span
content to see if the nickname is already used:
bool result = (ie.Span(Find.ById("lblResult")).Text == "The nickname is not used");
Assert.AreEqual(true, result);
Conclusion
At this stage, we should know how to basically use WatiN framework to test web applications. I hope you find this framework and this article useful, as I did. Personally, I haven't used any other web application testing framework, but I got really surprised with how we can test our UI using WatiN. I think that we can do intensive UI and functional testing with it.
References & Resources
References:
Resources: