65.9K
CodeProject 正在变化。 阅读更多。
Home

WatiN - .NET 中的 Web 应用程序测试

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (29投票s)

2007年1月8日

CPOL

6分钟阅读

viewsIcon

652874

downloadIcon

5130

本文介绍了如何使用 WatiN 以及如何为 Web 应用程序创建 UI 测试。

WatiN Test Screenshoot

引言

如今,测试代码并非难事。我们有许多工具可以帮助我们实现这一目标。但在 Web 应用程序开发中,我们往往会忽略测试最重要的层之一,即 UI。在本文中,我将简要介绍如何使用 WatiN(.NET 中的 Web 应用程序测试)进行 UI 和功能测试。我将尝试从开发者的角度展示这个框架给我带来的启发。

什么是 WatiN?

WatiN 是“Web Application Testing In .NET”的缩写,它是一个通过 Internet Explorer 进行 Web 应用程序测试的框架。WatiN 的灵感来源于 WatiR,这是 Ruby 的一个 Web 应用程序测试框架。WatiN 的工作方式非常“简单”,考虑到像这样的框架背后付出了大量的努力。WatiN 允许您打开 Internet Explorer 实例,并通过互操作与表单中的元素进行交互。使用 WatiN,您可以获取和设置表单中元素的值,还可以触发文档中任何元素的事件。

入门

WatiN 的 API 非常用户友好,只需查看一些代码示例并深入研究一下,您就可以快速受益于这个框架。使用 WatiN 构建测试方法时,我们需要做的第一件事是添加对 WatiN.Core.dll 的引用。WatiN 提供了四个命名空间;我们用于与 IE 实例交互的命名空间是 WatiN.Core。可用的不同命名空间有:

  • WatiN.Core.DialogHandlers:该命名空间提供了管理浏览器可能向用户显示的对话框所需的对象。您在此命名空间中找到的一些处理程序是,例如:AlertDialogHandlerConfirmDialogHandlerFileUploadDialogHandlerPrintDialogHandlerLogonDialogHandler
  • WatiN.Core.Exceptions:该命名空间包含特定异常,有助于我们控制任何不期望的行为。这些异常的一些示例是:ElementNotFoundExceptionIENotFoundExceptionTimeoutException 以及通用的 WatiNException
  • WatiN.Core.Interfaces:该命名空间提供了一些接口来扩展 WatiN 框架,例如,ILogWriter 接口允许我们实现自己的 LogWriter 类。
  • WatiN.Core.Logging:该命名空间包含帮助我们记录代码执行操作的类。我们可以使用 DebugLogWriter 类将日志消息写入调试窗口,或者使用 Logger 类以其他方式保存我们的日志。

由于本文的范围是介绍性的,我将只向您展示框架中最重要对象的用法。要了解有关其他命名空间和/或对象的更多信息,您可以查看本文底部的“参考和资源”部分。

现在我们对 WatiN 是什么以及我们可以用它做什么有了初步的了解,让我们编写一些代码来了解 API 的用法。

using WatiN.Core;

[STAThread]
static void Main(string[] args)
{
    // create a new Internet Explorer Instance

    IE ie = new IE();

    // point it to http://www.google.com

    ie.GoTo("http://www.google.com");

    // fill the search box

    ie.TextField(Find.ByName("q")).TypeText("WatiN");

    // performs click event

    ie.Button(Find.ByValue("Google Search")).Click();
}

上面的代码示例打开了一个 Internet Explorer 实例并将其指向 www.google.com,然后,在页面加载后,它会查找一个名为“q”的文本框并用文本“WatiN”填充它。然后,我们对文本框执行相同的操作,但这次查找一个值为“Google Search”的按钮。在这种情况下,我们执行按钮的“Click()”事件。如果我们运行这个控制台应用程序,我们将看到一个 Internet Explorer 打开并执行我们编写的代码。是不是很酷?

解决初始的变通方法

回到前面的代码片段,我不得不提几件事。为了自动化 Internet Explorer 的任务,我们需要在一个具有单线程单元(STA)作为单元状态的线程下运行我们的测试。请注意,在 Main() 方法上方,我们有 [STAThread] 属性,它将单元状态设置为 STA。当我接触到这个框架时,我问自己一个大问题:“在 NUnit 中运行这些测试怎么样?” NUnit 不在具有 STA 单元状态的线程中运行测试;它使用多线程单元(MTA),但这并不是一个大问题,因为我们可以将配置文件附加到 NUnit 测试项目并指定我们想要的单元状态模式。让我们看看如何做到这一点。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="NUnit">
            <section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
        </sectionGroup>
    </configSections>
    <NUnit>
        <TestRunner>
            <!-- Valid values are STA,MTA. Others ignored. -->
            <add key="ApartmentState" value="STA" />
        </TestRunner>
    </NUnit>
</configuration>

如果我们运行“入门”代码示例,Internet Explorer 将在我们编写的代码执行完毕后保持打开状态。如果我们只想运行一个测试,这对我们没有影响,我们只需要关闭一个 Internet Explorer 窗口。但是,如果我们想运行多个测试,我们可以做两件事来避免手动关闭 Internet Explorer 窗口。首先,我们可以使用 using 关键字创建 IE 对象实例,并使用允许我们指定新实例指向的 URL 的构造函数。如果我们这样做,它将在测试执行后关闭和处置。其次,我们可以调用 IE 实例的 Close() 方法以编程方式关闭窗口。两者都是有效的,但我更喜欢第一种,因为我认为其代码比使用第二种更优雅。

创建我们的第一个 UI 测试

如果我们的测试结果基于服务器发送回浏览器的响应,我们可以解析响应以确定我们的测试是否失败。我们执行此操作的方式与我们进行测试的方式完全相同,但查找页面中的其他项以帮助我们识别发生了什么。假设我们有一个 aspx 页面,如下所示:

<%@ 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>

现在,假设我们在 btnCheck.OnClick 事件中有以下代码:

protected void btnCheck_Click(object sender, EventArgs e)
{
    // do some logic to check if the nickname is already used.

    // i'll return true for all nicknames but "gsus"

    if (txtNickName.Text != "gsus")
        lblResult.Text = "The nickname is already in use";
    else
        lblResult.Text = "The nickname is not used";
}

为了编写这些文章的代码示例,我正在使用 Visual Studio 2005 在 Windows Vista 下运行,因此我没有安装 Internet Information Services。对于 Web 开发,我使用的是 .NET Framework 2.0 附带的 ASP.NET Development Server,因此在下一个代码示例中,您将看到我以编程方式启动 Web 服务器以运行测试。我认为这是将我们的 UI 测试与 IIS 解耦的好方法。现在,让我们看看最终的测试是什么样的。

using System.Diagnostics;
using WatiN.Core;
using NUnit.Framework;
using System;

namespace WebAppUITesting
{
    [TestFixture]
    public class UITesting
    {
        //process object 

        private Process p;

        [TestFixtureSetUpAttribute]
        public void SetUp()
        {            
            // create a new process to start
            // the ASP.Net Development Server

            p = new Process();

            // set the initial properties 

            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;

            // start the process

            p.Start();
        }

        [Test]
        public void CheckIfNicknameIsNotUsed()
        {
            // create a new Internet Explorer instance
            // pointing to the ASP.NET Development Server

            using (IE ie = new IE("https://:8080/Default.aspx"))
            {
                // Maximize the IE window in order to view test

                ie.ShowWindow(NativeMethods.WindowShowStyle.Maximize);

                // search for txtNickName and type "gsus" in it

                ie.TextField(Find.ById("txtNickName")).TypeText("gsus");

                // fire the click event of the button

                ie.Button(Find.ById("btnCheck")).Click();

                // parse the response in order to fail the test or not 

                Assert.AreEqual(true, ie.ContainsText("The nickname is not used"));
            }            
        }

        [TestFixtureTearDownAttribute]
        public void TearDown()
        {
            // kill the ASP.NET Development Server process

            p.Kill();
        }
    }
}

如您所见,我在第 46 行使用了 ContainsText 方法。此方法在文档内容中搜索指定的文本,并返回一个 bool 值,指示搜索中是否存在匹配项。我们可以用以下代码示例替换第 46 行,在该示例中,我们直接获取“lblResultSpan 的内容,以查看昵称是否已被使用:

// parse the response in order to fail the test or not 

bool result = (ie.Span(Find.ById("lblResult")).Text == "The nickname is not used");
Assert.AreEqual(true, result);

结论

至此,我们应该知道如何基本使用 WatiN 框架来测试 Web 应用程序。我希望您发现这个框架和这篇文章很有用,就像我一样。我个人没有使用过任何其他的 Web 应用程序测试框架,但我对如何使用 WatiN 测试我们的 UI 感到非常惊讶。我认为我们可以用它进行大量的 UI 和功能测试。

参考文献与资源

参考文献

资源

© . All rights reserved.