写点什么

使用 Selenium 和 Castle 进行测试驱动开发

  • 2007-03-29
  • 本文字数:3464 字

    阅读完需:约 11 分钟

引言

测试驱动开发(TDD,Test Driven Development)的例子大多是基于非常简单的单元测试。如何在大规模的应用中使用 TDD 常常是一项挑战。在本教程中,我们将展示如何使用 Selenium 和 Castle 以测试先行原理来构建一个 Web 应用。

前期准备

比方说,开发者需要应用“测试先行”的思想,为应用程序编写一种带有以下特征的方法:

管理用户(添加新用户,删除,编辑用户资料,所有用户列表)

在该测试用例中,每个用户都必须拥有一个全名、一个用户名、一个密码和一个邮件地址等,所有的信息都是必须的。

基本步骤

典型的步骤如下:

  1. 编写测试
  2. 确保测试失败
  3. 编写代码使测试成功
  4. 重构
  5. 重复以上步骤

第一个测试

第一个要进行的测试是添加新用户的测试。测试驱动开发与其说是测试的技术不如说是设计的技术,因为当编写测试的时候,我们将规定代码或页面的工作方式,这个过程就是设计。

对于添加一个新用户而言,像下面这样简单的表单就够了:

对于功能测试,开发者需要打开添加页面(准备阶段),填写表单并保存(执行阶段)和确认用户是否被保存了(项目的确认阶段)。为了做到这一点,开发者需要更新页面,在左侧添加一个新的包含用户信息的列表,以便在点击“保存”按钮后可以验证用户存在与否。

使用 Selenium

对于像这样的工作,开发者需要一个能够适合他们的执行这个行为的工具。Selenium 在浏览器中可以方便地做到这一点,它也是一个很好的开源工具,可以根据你自己的需要进行修改。Selenium 提供了基于 Web 的功能测试,而且只需要一个为开发人员运行这些行为的编译器,Selenium 就能使这些测试的编写过程像 HTML 测试那么简单:

对于希望将自己的测试整合到一个持续集成的工具中的开发者而言,最大的喜讯莫过于,他们可以用自己喜欢的语言(如 C#、JAVA、VB.NET、Ruby 或者 Python 等)来编写测试,并利用 Selenium 的一个名为 Selenium RC 的扩展实现整合。

比如使用 Selenium RC 时,该测试的.NET 版如下:

第二步,保证初始的测试失败

在这一阶段开发者没有写任何代码,因此测试失败。 首先启动 Selenium RC 服务器(一个处理 Selenium 指令并将它们传送给浏览器的小型 java 服务器):

java -jar selenium-server.jar

意料之中,运行的测试失败:

这是一个好现象,因为这意味着测试在应该失败时失败了。否则这次测试就没有起到任何作用,是毫无意义的。

第三步,编写代码

在 TDD 实施步骤的第三步中,开发者需要编写代码。这意味着当反向测试时,代码应该不会运行失败。下一步建立 User 控制器,然后建立视图并运行测试:

接着再建立一个空的 add.vm,并且重新运行该测试:

Selenium.SeleniumException: ERROR: Element link=Add new user not foundat Selenium.HttpCommandProcessor.DoCommand(String command, String[] args)

at Selenium.DefaultSelenium.Click(String locator)
at MRProjectTest.Functionals.Selenium.ManageUsersTests.TestAddNewUser() in
ManageUsersTests.cs:line 34

因为错误报告说在网页上找不到内容,所以我们就把内容添加到 add.vm 中:

重新测试:

错误再次出现,因为将表单的内容提交到 create.aspx 之后,点击页面按钮的行为尚未实现。

然后添加如下代码以保存数据:

现在我们稍等片刻,因为无论 list 行为还是数据库中都不存在 User 类。

在表示层下对其它层进行 TDD

为了构造代码,开发者需要根据“测试先行”构造 User 类。 虽然在有的情况下这没有必要,因为 ActiveRecord 已经很好地被测试过了,并且它也通过了功能测试。 但仍需指明应该如何处理一些更为复杂的情况。

接下来的测试不是功能测试,而是集成测试,一个利用数据库的单元测试:

测试它会不会失败。其实它甚至没有进行编译,所以首先是构造一个没有任何方法的 User 类,强制编译其代码:

现在,运行测试:

Castle.ActiveRecord.Framework.ActiveRecordException: An ActiveRecord class (UserManagement.Model.User) was used but the framework seems not properly initialized. Did you forget about ActiveRecordStarter.Initialize() ?at Castle.ActiveRecord.ActiveRecordBase.EnsureInitialized(Type type)

at Castle.ActiveRecord.ActiveRecordBase.Save(Object instance)
at Castle.ActiveRecord.ActiveRecordBase.Save()
at MRProjectTest.Database.UsersDataAccessTests.TestSaveNewUser()
in UserDataAccessTest.cs:line 23

错误表明 User 类没有在 ActiveRecord 中初始化,因此对测试调整如下:

为 ActiveRecord 与构造器加上适当的属性,并且重新运行测试。现在相应的数据库表还没有,但这可以在测试中添加如下代码迅速进行补救:

ActiveRecordStarter.CreateSchema();// 创建数据库 schema

运行完测试后,数据库表就生成了,但仍有一个问题:

System.NotImplementedException: todoat UserManagement.Model.User.Find(Int64 id) in User.cs:line 72

at MRProjectTest.Database.UsersDataAccessTests.TestSaveNewUser() in
UserDataAccessTest.cs:line 41

完成对 User 类 Find() 方法的实现:

<span color="#0000ff"> public static User Find(long id)<br></br> {<br></br> return (User) FindByPrimaryKey(typeof(User),id,false)<br></br> }</span>终于,一个数据库测试能够运行了!

自顶而下的 TDD 方法

通常,由于前面提到的两个原因(译者注:ActionRecord 已经很好的被测试过,并且通过了功能测试),这类数据库测试是不需要的,但是我们还是做了这种测试,因为在一个针对 n 层应用的测试先行的垂直开发环境中,自顶而下的 TDD 流程才能被理解。

回到功能测试

既然 User 类存在并且数据库访问正常,是时候继续进行表示层的工作了。

实现 list 的行为和视图:

<span color="#0000ff"> public void List()<br></br> {<br></br> PropertyBag["users"] = User.FindAll();<br></br> }</span>创建一个 list.vm:

上面的视图中使用了 GridComponent。现在运行测试,开发者应该会首次看到正常运行的 UI 测试。

编辑功能

下一步就需要给网站增加用户编辑的功能。 功能的流程是这样的:在用户列表页面,每一个用户都有一个编辑链接,一旦用户点击链接后会转到编辑页面,在那里可以修改用户的详细信息。当表单保存后,用户就返回到列表页面。现在编写测试:

一个用户被添加到了数据库,当列表网页被打开的时候,就有内容可以编辑了。但还有一个问题。如果测试运行两次,那么同一个用户将在数据库中被插入两次。为了避免这种潜在的错误,照下面这样做就可以了:

运行所有的测试,编辑的测试现在是失败的:

Selenium.SeleniumException: ERROR: Element link=Edit not foundat Selenium.HttpCommandProcessor.DoCommand(String command, String[] args)

at Selenium.DefaultSelenium.Click(String locator)
at MRProjectTest.Functionals.Selenium.ManageUsersTests.TestEditUser() in
ManageUsersTests.cs:line 28

为解决这个问题,在 list.vm 中添加编辑链接:

然后修改控制器中的行为:

<span color="#0000ff"> public void Edit(long id)<br></br> {<br></br> PropertyBag["user"] = User.Find(id);<br></br> }</span>现在编辑这个行为的视图:edit.vm

因为值同样会被保存到 update 行为中,我们还会有下面的代码:

<span color="#0000ff"> public void Update([DataBind("user")] User user)<br></br> {<br></br> user.Update();<br></br> RedirectToAction("list");<br></br> }</span>

成功了!!

开始重构

还有机会在一些地方进行重构。首先,TestAddNew 和 TestEdit 方法几乎相同:

同时:

运行测试,一切照样运行。现在进一步研究展示视图中的代码,它们有同样的问题:add.vm 和 edit.vm 几乎如出一辙。 将公用部分单独放入 _form.vm。运行测试依然可以确定该应用程序是正常工作的:

对于删除操作,同样可以利用“测试先行”的原理。对于数据验证或者用户可以使用的任何功能,添加新的测试,然后加入代码最终使其通过这些测试。

结论

这是一个使用增量体系结构的方法来利用 TDD 设计应用的例子。在实际的系统体系结构中,架构师与开发者不需要提前一个月进行设计。在代码被编写与测试时,架构和设计才被构建起来。在这种情况下,经常会有变化,因为持续重构代码可以使代码更加优化,这些都是 TDD 的原则与“测试先行”所支持的。

Resources 资源:

  1. Selenium - http://www.openqa.org :开源网站功能测试工具
  2. Castle Project (MonoRail and ActiveRecord) - http://www.castleproject.org/ :开源的轻量级 ASP.NET /ADO.NET 选择

源代码下载

关于译者

王川川,混迹于武汉大学 Google Camp ,热衷于 Web 开发,目前关注 IBM 的 portal 产品相关开发。

2007-03-29 08:083327

评论

发布
暂无评论
发现更多内容

6月各手机银行活跃用户较快增长,创半年新高

易观分析

数据分析 金融 电子银行

从Delta 2.0开始聊聊我们需要怎样的数据湖

网易数帆

大数据 数据湖 Arctic 湖仓一体

企业即时通讯是什么?可以应用在哪些场景?

WorkPlus

开源一夏 | mysql5.7 安装部署 -二进制安装

zhangpfly

MySQL 开源 MySQL 运维 #开源 8月月更

RocketMQ 消息集成:多类型业务消息——定时消息

阿里巴巴云原生

阿里云 RocketMQ 云原生 消息队列

产品说明丨Android端使用MobPush快速集成方法

MobTech袤博科技

android Android Studio 集成 mobpush

【C#】WCF和TCP消息通信练习,实现群聊功能

南蓬幽

签约计划第三季 8月月更

【接入指南 之 直接接入】手把手教你快速上手接入HONOR Connect平台(下)

荣耀开发者服务平台

手机 新手指南 安卓 荣耀 honor

接口测试进阶接口脚本使用—apipost(预/后执行脚本)

Xd

Java 接口测试

120Hz OLED拒绝“烧屏”!华硕无双全能轻薄本

科技热闻

MSE 治理中心重磅升级-流量治理、数据库治理、同 AZ 优先

阿里巴巴云原生

数据库 阿里云 微服务 云原生 限流

直播回顾|多云时代,如何建设企业级云管理平台?(附建设指南下载)

BoCloud博云

云计算 容器 云平台 云管理

shell运算详解,看这一篇就够了!

Albert Edison

Linux centos 运维 shell脚本编程 8月月更

Apache DolphinScheduler 3.0.0 正式版发布!

白鲸开源

海豚调度 DolphinScheduler 调度器 版本发布

Java System.lineSeparator 方法

HoneyMoose

What's new in dubbo-go-pixiu 0.5.1

apache/dubbo-go

dubbogo Dubbo3 Dubbo网关

RocketMQ整体架构与基本概念

急需上岸的小谢

8月月更

网络可观测性:让您的网络监控更上一层楼|TechGenix

观测云

选择是公有云还或是私有云,这很重要吗?

WorkPlus

从企业的视角来看,数据中台到底意味着什么?

WorkPlus

开源一夏 | 提高代码可重用性,减少重复劳动--手把手带你实现Python自定义模块并上传到pypi,贡献自己创造的轮子为所有人使用,让Python开发更加简单

迷彩

Python 开源 签约计划第三季 8月月更 自定义模块

每日一R「03」Borrow 语义与引用

Samson

8月月更 ​Rust

兼具外观、性能、屏幕!华硕灵耀X 14火热抢购中

科技热闻

什么是企业知识库?有什么作用?如何搭建?

金陵老街

vue cli 知识库 spring-boot

搭载2.8K 120Hz OLED华硕好屏 无畏Pro15 2022锐龙版屏开得胜

科技热闻

CEO对今天的CIO们真正的要求是什么?

WorkPlus

云渲染的应用正在扩大,越来越多的行业需要可视化服务

Finovy Cloud

云渲染 GPU渲染

机器学习模型验证:被低估的重要一环

澳鹏Appen

人工智能 机器学习 模型开发 模型开发训练 模型验证

AIRIOT答疑第8期|AIRIOT的金字塔服务体系是如何搞定客户的?

AIRIOT

低代码 物联网 低代码,项目开发

测试开发【Mock 平台】08 开发:项目管理(四)编辑功能和Component抽离

MegaQi

8月月更

HarmonyOS自动化测试框架—Hypium

HarmonyOS开发者

HarmonyOS

使用Selenium和Castle进行测试驱动开发_研发效能_Dan Bunea_InfoQ精选文章