19

我正在寻找改进网站功能测试自动化过程的建议。这是我过去尝试过的。

我曾经有一个使用WATIN的测试项目。您有效地编写了看起来像“单元测试”的内容,并使用 WATIN 自动化浏览器以点击您的网站等。

当然,您需要一个站点才能运行。所以我让测试实际上将代码从我的 web 项目复制到本地目录,并在任何测试运行之前启动了一个指向该目录的 web 服务器。

这样,新人可以简单地从我们的源代码控制中获取最新信息并运行我们的构建脚本,并查看所有测试的运行情况。他们还可以简单地从 IDE 运行所有测试。

我遇到的问题是我花了很多时间来维护代码来设置测试环境,而不是测试。更不用说由于所有的复制需要很长时间才能运行。此外,我需要测试包括安装在内的各种场景,这意味着我需要能够将数据库设置为各种初始状态。

我很好奇你做了什么来自动化功能测试来解决其中一些问题并保持简单。

更多细节 由于人们要求更多细节,这里就是。我正在使用 Visual Studio 和 Cassini(内置 Web 服务器)运行 ASP.NET。我的单元测试在 MbUnit 中运行(但这并不重要。可能是 NUnit 或 XUnit.NET)。通常,我有一个单独的单元测试框架来运行我所有的 WATIN 测试。在 AssemblyLoad 阶段,我启动 Web 服务器并在本地复制我的所有 Web 应用程序代码。

我对任何平台的解决方案都感兴趣,但我可能需要更多关于每件事的含义的描述。:)

4

6 回答 6

11

菲尔,

自动化可能很难维护,但是您使用自动化进行部署的次数越多,您就越可以利用它进行测试设置(反之亦然)。

坦率地说,当使用不仅仅是驱动静态编译的、预先分解的功能单元的构建工具时,更容易发展自动化代码,将其分解并重构为特定的小功能
单元,就像 NAnt 和微软构建。这是许多相对较早使用 NAnt 等工具的人转而使用 Rake 的原因之一。将构建代码视为任何其他代码的自由度 - 不断发展其内容和形状 - 使用 Rake 更大。您不会像使用 Rake 那样轻松快速地在自动化工件中遇到同样的停滞,而且在 Rake 中编写脚本比 NAnt 或 MSBuild 容易得多。

所以,你的斗争的某些部分本质上与工具有关。为了保持自动化的合理性和维护性,您应该警惕静态构建工具(如 NAnt 和 MSBuild)施加的障碍。

我建议您不要将测试环境引导与程序集负载结合起来。这是一个由内而外的耦合,只提供短暂的便利。在从 IDE 或从命令行或从交互式控制台(如 C# REPL Mono 项目,或来自 IRB。

测试数据设置有时只是一件麻烦事。它必须完成。

您将需要一个可以调用的库来创建和清理数据库状态。您可以直接从您的测试代码中进行这些调用,但我个人倾向于避免这样做,因为测试数据或示例数据控制代码的用途不止一种。

我从 HTTP 驱动所有示例数据控制。我编写带有专门用于控制示例数据的操作的控制器,并通过 Selenium 针对这些操作发出 GET。我使用这些来创建和清理数据。我可以将 GET 组合到这些操作中以创建设置数据的常见场景,并且我可以将数据的特定值作为请求参数(或如果需要,也可以是表单参数)传递。

我将这些控制器放在我通常称为“test_support”的区域中。

我部署网站的自动化没有部署 test_support 区域或其路由和映射。作为部署验证自动化的一部分,我确保 test_support 代码不在生产应用程序中。

我还使用 test_support 代码来自动控制整个环境——用伪造品替换服务、关闭子系统以模拟故障和故障转移、激活或停用与这些方面无关的功能测试的身份验证和访问控制等。

控制 Web 应用程序的样本数据或来自 Web 的测试数据有一个很大的次要价值:在演示应用程序或进行探索性测试时,您可以通过针对已知(或可猜测)的 url 发出一些获取来创建所需的数据场景在 test_support 区域。在这里真正努力坚持宁静的路线和资源导向会真正得到回报。

这种功能自动化(包括测试、部署、演示等)还有很多其他功能,因此这些资源设计得越好,您在长长的大厅中维护它们的时间就越多,您会发现更多的机会以不可预见但有益的方式利用它们。

例如,在网页的语义模型上编写域模型代码将有助于创建更易于理解的测试代码并降低脆弱性。如果你做得好,你可以将这些相同的模型与各种不同的驱动程序一起使用,这样你就可以在压力测试和负载测试以及功能测试中利用它们,也可以从命令行将它们用作探索性工具。顺便说一句,当您不像使用静态语言时那样绑定到驱动程序类型时,这种事情更容易做到。许多领先的测试思想家和实干家使用 Ruby 工作,以及为什么 Watir 是用 Ruby 编写的,这是有原因的。与 C# 测试代码相比,在 Ruby 中实现重用、组合和表现力要容易得多。但那是另一回事了。

让我们找个时间再聊聊其他 90% 的东西 :)

于 2009-08-08T06:33:28.157 回答
2

我们在一个项目中使用了Plasma 。它在进程中模拟 Web 服务器 - 只需将其指向您的 Web 应用程序项目的根目录。

它出奇地稳定——无需复制文件或启动进程外服务器。

以下是使用 Plasma 进行的测试如何寻找我们...

    [Test]
    public void Can_log_in() {
        AspNetResponse response = WebApp.ProcessRequest("/Login.aspx");
        AspNetForm form = response.GetForm();

        form["UserName"] = User.UserName;

        form["Password"] = User.Password;

        AspNetResponse loggedIn = WebApp.ProcessRequest(Button.Click(form, "LoginUser"));


        Assert.IsTrue(loggedIn.IsRedirect());

        AspNetResponse homePage = WebApp.ProcessRequest(loggedIn.GetRedirectUrl());

        Assert.AreEqual(homePage.Status, 200);
    }

Plasma 包含所有“AspNetResponse”和“AspNetForm”类。

于 2009-08-08T14:40:32.823 回答
1

我们目前正在为我们的 asp.net mvc 应用程序使用自动构建过程。

我们使用以下工具:

  • 团队城市
  • SVN
  • 单位

我们使用在构建代理上运行的 msbuild 脚本,该构建代理可以是任意数量的机器。msbuild 脚本从 svn 获取最新版本的代码并构建它。

成功后,它将工件部署到给定的机器/文件夹并在 IIS 中创建虚拟站点。

然后我们使用 MSBuild contrib 任务运行 sql 脚本来安装数据库和加载数据,您也可以进行还原。

成功后,我们开始了 nUnit 测试。测试设置确保 selenium 启动并运行,然后以与 Watin 相同的方式驱动 selenium 测试。Selenium 有一个很好的测试记录器,可以导出到 c#。

Selenium 的好处是你可以驱动 FF、Chorme 和 IE,而不是像我上次看到 Watin 那样被限制在 IE 上。您还可以使用 Selenium 通过 Selenium Grid 进行负载测试,因此您可以重用相同的测试。

成功后,msbuild 然后在 svn 中标记构建。TeamCity 有一个通宵运行的作业,它将最新的标签部署到一个准备好让业务用户在第二天早上检查项目状态的登台环境。

在以前的生活中,我们有 nant 和 msbuild 脚本来完全管理环境(安装 java、selenium 等),但这确实需要很多时间,因此作为前提条件,我们假设每个构建代理都安装了这些。随着时间的推移,我们将包括这些任务。

于 2009-08-08T07:03:20.610 回答
1

为什么需要复制代码?抛弃 Cassini,让 Visual Studio 为您创建一个虚拟目录。如果 web 应用程序发生了变化,开发人员必须记住在运行 web 测试之前构建。我们发现这没什么大不了的,特别是如果您在 CI 中运行 Web 测试。

数据是一个巨大的挑战。据我所知,您必须在不完美的选择之间做出选择。以下是我们如何处理它。首先,我应该解释一下,我们正在使用一个大型复杂的遗留 WebForms 应用程序。另外我应该提到,域代码不适合从测试项目中创建测试数据。

这给我们留下了几个选择。我们可以:(a) 在构建下运行数据设置脚本,或 (b) 通过使用实际网站的 Web 测试创建所有数据。选项 (a) 的问题是测试与脚本在分钟级别上耦合在一起。一想到将 Web 测试代码与 T-SQL 同步,我就头疼。所以我们选择了(b)。

(b) 的一个好处是您的设置还可以验证应用程序的行为。问题是……时间

理想情况下,测试应该是独立的,没有时间耦合(可以以任何顺序运行)并且不共享任何上下文(例如,公共测试数据)。处理此问题的常用方法是在每次测试时设置和删除数据。经过深思熟虑,我们决定打破这个规则。

我们使用 Gallio (MbUnit 3),它提供了一些很好的特性来支持我们的策略。首先,它允许您在夹具和测试级别指定执行顺序。我们有四个“设置”装置,它们的顺序是 -4、-3、-2、-1。它们以指定的顺序运行,并且在所有“非设置”装置之前运行,默认情况下,它们的顺序为 0。

我们的 Web 测试项目仅依赖于一件事的构建脚本:一个众所周知的用户名/密码。这是我可以忍受的耦合。随着设置测试的运行,他们构建了一个“数据上下文”对象,该对象包含数据(公司、用户、供应商、客户等)的标识符,以后在其他所有设备中使用(但从未更改)。(通过标识符,我不一定指键。在大多数情况下,我们的 Web UI 不会公开唯一键。我们必须使用名称或其他代理来导航应用程序以获取真正的标识符。更多内容见下文。)

Gallio 还允许您指定一个测试或夹具依赖于另一个测试或夹具。当先例失败时,将跳过依赖项。这通过防止可能导致很多混乱的“级联故障”来减少时间耦合的弊端。

一次创建基线测试数据,而不是在每次测试之前创建,这会大大加快速度。但是,设置测试仍可能需要 10 分钟才能运行。当我进行新测试时,我想经常运行并重新运行它们。输入另一个很酷的 Gallio 功能:氛围。Ambience 是围绕 DB4 的包装器,它提供了一种非常简单的方法来持久化对象。我们使用它来自动持久化数据上下文。因此,设置测试只能在数据库重建之间运行一次。之后,您可以重复运行任何或所有其他固定装置。

那么清理测试数据呢?我们不需要从已知状态开始吗?这是我们发现打破的规则。一个对我们有效的策略是对公司名称、用户名等使用长随机值。我们发现将测试运行保持在逻辑“数据空间”内并不是很困难,这样它就不会碰撞到其他数据中。当然,我担心有一天我会花几个小时追查一个假想失败的测试,结果却发现这是一些数据冲突。这是目前为我们工作的权衡。

我们正在使用Watin。我挺喜欢它的。另一个成功的关键是 Scott Bellware 提到的。在创建测试时,我们正在构建 UI 的抽象模型。所以代替这个:

browser.TextField("ctl0_tab2_newNote").TypeText("foo");

您将在我们的测试中看到这一点:

User.NotesTab.NewNote.TypeText("foo");

这种方法提供了三个好处。首先,我们从不重复魔术字符串。这大大降低了脆性。其次,测试更容易阅读和理解。最后,我们将大部分 Watin 框架隐藏在我们自己的抽象后面。在第二个示例中,只有 TypeText 是 Watin 方法。这将使其更容易随着框架的变化而变化。

希望这可以帮助。

于 2009-08-08T19:39:53.143 回答
0

使用 maven 将集成测试阶段构建到构建过程中是困难的,但并非不可能。发生的事情基本上是这样的:

  • 忽略特定目录中的所有 JUNit 测试,除非集成测试阶段触发。
  • 添加一个 Maven 配置文件来执行集成测试。
  • 对于预集成测试阶段 -

  • 启动 Jetty 运行应用程序并访问测试数据库。

  • 启动硒服务器
  • 在集成测试阶段运行 selenium 集成测试
  • 停止硒服务器
  • 停止硒

这一步的困难在于设置码头——我们不能让它从战争中启动,所以我们实际上必须让码头解开战争,然后运行服务器——但它工作得很好,而且是自动化的——您所要做的就是输入 mvn -PintegrationTest (这是我们的集成测试配置文件名称)然后关闭它。

于 2009-08-06T16:45:12.027 回答
0

你的意思是构建完成后自动开始测试?您可以编写自动化脚本以在构建成功编译的同时将构建文件复制到正常工作的 IIS。然后通过调用 mstest.exe 或其他方法启动自动化 BVT。

您可以尝试使用 autoitx 或一些函数语言,例如 Python、ruby。

于 2009-08-12T07:33:26.690 回答