刚刚以SCRUM的方式结束了一个的ASP.NET网站的测试的第一个Spring,因为团队从无到有实现自动化测试系统,有必要把这次的经验和教训总结一下,以便后续的Spring可以获取一些有意义的借鉴。

  因为是一个技术博客,所以使用SCRUM管理这个测试项目的经验放在别的地方分享,这系列的文章分享一下使用VSTT和Selenium结合实现自动化测试系统的经验。

  Selenium简介

  Selenium主要是一个录制并回放的自动化测试用例编制工具,由一个录制工具Selenium IDE(一个Firefox插件,当然这个工具也可以回放啦),一个回放工具Selenium Remote Control在其他机器和其他操作系统上进行回放。Selenium的一个好处是你可以使用它测试所有操作系统下的所有主流浏览器,至于Linux下面的konqueror和gnome下面自带的浏览器,没有试过Selenium是否支持,当然那个控制台界面下的浏览器更没有试过啦。Selenium还有一个Selenium Grid,据说很强大,因为项目比较紧,没有花时间去看它。

  至于Selenium各个工具的用法,它的官网上有详细的文档,如果文档也没说清楚的话,那直接读源代码吧。

  Selenium和VSTT的整合

  Selenium可以根据录制的步骤生成直接在NUnit中使用的C#代码,这些代码基本上都可以在VSTT中直接使用,是一些属性需要更改。例如[TestFixture]改成[TestClass],[Test]改成[TestMethod]之类的,改好以后,启动Selenium-RC,可以直接在VSTT里面当作普通的单元测试用例执行了。

  Selenium代码优化

  既然要做自动化测试,那么有一点是必须要时刻考虑的,是在产品开发过程中,程序界面甚至是内部的类库接口也是时刻改变的。而Selenium只能记录当时录制测试用例的界面情况,因此需需要将它生成的代码分解一下,以面向对象的方式来重写。例如下面这段代码的目的是测试用户可以查看自己的博客:


  [TestMethod]

  public void TheTestTest()

  {

  selenium.Open("/");

  selenium.Click("link=登录");


  selenium.WaitForPageToLoad("30000");


  selenium.Type("tbUserName", "donjuan");

  selenium.Type("tbPassword", "");

  selenium.Click("btnLogin");


  selenium.WaitForPageToLoad("30000");

  selenium.Click("link=donjuan");

  selenium.WaitForPageToLoad("30000");


  selenium.Click("link=博客");


  selenium.WaitForPageToLoad("30000");


  }


  但是网页页面布局,或者Html控件的Id、文本等内容随时都会被程序员修改,修改的原因有多种,例如修复新的错误(Bug),或者仅仅是代码重构。因此作为测试团队,不能总是认为网页的内容一成不变的。而象登录这种操作,大部分测试用例都会用到,所以好只要为登录动作创建的代码 。有多个方案:


  1. 为登录创建一个独立的测试用例,本来登录这个功能是要测试的嘛,在编辑自动化测试用例列表的时候,把登录用例放在前面。


  2. 为登录动作创建一个单独的函数,例如LogOn(),然后在其他测试用例当中(包括登录的测试用例)调用这个函数,另外,因为可能会需要用到不同的 用户,所以好把用户名和密码等变量提取出来,变成LogOn(string username, string password)之类的函数。


  两个方案,显然是第二个方案的弹性大,但是对于第一个方案,如果测试人员都是新手,且对代码不熟悉的话,建议可以考虑。


  于是我们的代码变成类似下面的代码:

  using System;


  //


  // 这个异常是故意创建出来,用来封装所有在测试代码中发生的错误

  //

  public class CaseErrorException : Exception


  {


  public CaseErrorException(string message)


  : base()


  {


  }

  public CaseErrorException(Exception inner)


  : this(null, inner)


  {


  }

  public CaseErrorException(string message, Exception inner)

  : base(message == null ? "测试代码错误,请修复测试代码,查看InnerException属性!" :


  string.Format("测试代码错误,请修复测试代码,详细错误信息:{0};或者查看InnerException属性!", message),


  inner)


  {

  }

  }

  public class UserOperationsHelper

  {

  public void LogOn(string username, string password)

  {

  // string.Empty留出来为测试目的服务

  if (username == null)


  throw new CaseErrorException(new ArgumentNullException("username"));

  if (password == null)

  throw new CaseErrorException(new ArgumentNullException("password"));

  selenium.Open("/");

  selenium.Click("link=登录");

  selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);

  selenium.Type("tbUserName", username);


  selenium.Type("tbPassword", password);


  selenium.Click("btnLogin");

  selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);


  }

  }

  public static class Consts


  {


  // 将等待的时间提取成一个公开的函数,因为在今后大规模的测试

  // 过程中,很多自动化测试用例不简单地执行,会导致网站响应速度