。。。

基于Selenium和VSTT的网站测试自动化系统

上一篇 / 下一篇  2011-01-16 18:39:45

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
{
    // 将等待的时间提取成一个公开的函数,因为在今后大规模的测试
    // 过程中,很多自动化测试用例不简单地执行,会导致网站响应速度
    // 变慢,所以
    public const string TimeToWaitForPageLoad = "30000";
}
 
public class TestLibrary
{
    public UserOperationsHelper UserHelper { get; private set; }
}
 
public class TestClass
{
    [TestMethod]
    public void LogOnTest()
    {
        var username = "donjuan";
        var password = "它是个秘密";
        TestLibrary.UserHelper.LogOn(username, password);
 
        // 在测试过程中,我们发现这个链接是
        // 根据用户名而变的,为了扩展性,动态生成其标识文本
        selenium.Click(string.Format("link={0}", username));
        selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);
        selenium.Click("link=博客");
        selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);
 
        // 执行一些必要的测试验证过程
        Assert.IsTrue(selenium.IsTextPresented(...));
    }
}
 
这里稍微解释一下,创建自动化测试代码,就是为了节省手工重复测试的工作量以及测试失误的风险。但只要是代码,都会有可能出错,因此自动化测试框架里面创建了一个CaseErrorException,这样在每次分析测试用例失败的时候,可以一眼区分开测试代码的错误和产品代码中的错误。例如在UserOperationHelper.LogOn函数中的参数检查,当然啦,在测试过程当中,有可能需要测试不输入用户名或者密码的情况下,验证登录界面是否正常工作的情况。因此在验证参数的时候,特意为这种情况留下了String.Empty的入口,而对于null值,则基本上可以判断是因为测试人员在编写代码上的失误(具体原因会在数据驱动测试里面讲到)。

至于TestLibrary的初始化,完全可以放到每一个测试类型的TestInitializer里面,如下表所示: 
[TestClass]
public class AddBlogTest
{
    private TestContext testContextInstance;
    public TestContext TestContext
    {
        get
        {
                return testContextInstance;
        }
        set
        {
            testContextInstance = value;
        }
    }
 
    private TestLibrary TestLibrary;
    private ISelenium selenium;
 
    [TestInitialize]
    public void SetupTest()
    {
        TestLibrary = TestLibrary.SetupTest(TestContext);
        selenium = TestLibrary.Selenium;
    }
 
    [TestCleanup]
    public void TeardownTest()
    {
        TestLibrary.Shutdown();
    }
}
 
咋看起来,把LogOn测试用例分解成那么多的类型,有点画蛇添足,实际上这些函数库正是为了更方便地创建后续的测试用例耗费的磨刀的功夫。例如下面的代码是基于一些创建好了的函数编写的测试用例: 
[TestMethod]
public void CreateBlog()
{
    TestLibrary.UserHelper.LogOnAsAdmin();
    var blog = TestLibrary.BlogHelper.CreateBlog("博客的标题", "博客的链接");
 
    selenium.Click("link=管理博客");
    selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);
 
    Assert.IsTrue(selenium.IsElementPresent(string.Format("link={0}", blog.Title)));
}
 

TAG:

 

评分:0

我来说两句

日历

« 2024-04-26  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 2104
  • 日志数: 5
  • 建立时间: 2010-12-14
  • 更新时间: 2011-02-10

RSS订阅

Open Toolbar