在单元测试中应用数据驱动

作为自动化测试的一种脚本技术,数据驱动技术在10几年前已经有人提出,并且沿用到现在。Nunit作为C#开发下的一种常用的单元测试框架,也是被广泛使用。但是对于单元测试,很多时候都是硬编码,虽然说用Nunit编写单元测试代码是挺廉价的,但是为什么不让自己的工作更加轻松,脚本更加灵活呢?

我把单元测试的数据放到单独的XML文件里面,测试运行的时候就读取XML数据然后进行测试。由于我想做一个轻量级的单元测试,所以并没有把很多判断控制的语句写到一个单元测试里面,而是把有不同行为的测试写到一个脚本里面。例如期望结果都是成功的一类测试就写到一个脚本,包含同样错误的测试就写到另外一个脚本,尽管这样看起来并不是很高明,但是对于我的工作来说这是足够的了。

首先是XML文件的结构,其实很简单:



  
    
      111
      222
      True
      User album counter should greater equal than 0, the actual value is 
      True
      User photo counter should greater equal than 0, the actual value is 
      True
      Return user ID is not valid, the actual value is 
    
    
      222
      111
      True
      User album counter should greater equal than 0, the actual value is 
      True
      User photo counter should greater equal than 0, the actual value is 
      True
      Return user ID is not valid, the actual value is 
    
  

根节点是TestData,它的子结点是TestXMLD,这个节点是跟我的单元测试方法同名的。这个节点下面有2个子结点,分别是两套测试的数据,每套测试的数据分别包含了测试运行时候用到的数据,期望结果以及测试不能通过时候所要显示的错误提示。而且每套数据都包含一个isEnable的属性,如果设为False,那么测试运行的时候就不跑这一条数据。

测试的方法如下:

[TestMethod]
public void TestXMLD()
{
     XmlDocument xd = new XmlDocument();
     xd.Load(@"TestData\AlbumService.xml");
     string testDataNode = System.Reflection.MethodBase.GetCurrentMethod().Name;
     XmlNode testdata = xd.SelectSingleNode("/TestData/" + testDataNode);
     XmlNodeList testdatas = testdata.ChildNodes;
     foreach (XmlNode datanode in testdatas)
     {
         //Get the enabled test data
         bool isEnable = Convert.ToBoolean(datanode.Attributes.GetNamedItem("isEnable").InnerText);
         if (!isEnable)
         {
             break;
         }
         //Prepare test data.
         int userId = Convert.ToInt32(datanode.SelectSingleNode("UserID").InnerText);
         int FriendID = Convert.ToInt32(datanode.SelectSingleNode("FriendID").InnerText);
         bool AlbumCounterExpected = Convert.ToBoolean(datanode.SelectSingleNode("AlbumCounterExpected").InnerText);
         string AlbumCounterOutput = datanode.SelectSingleNode("AlbumCounterOutput").InnerText;
         bool PhotoCounterExpected = Convert.ToBoolean(datanode.SelectSingleNode("PhotoCounterExpected").InnerText);
         string PhotoCounterOutput = datanode.SelectSingleNode("PhotoCounterOutput").InnerText;
         bool ReturnIDExpected = Convert.ToBoolean(datanode.SelectSingleNode("ReturnIDExpected").InnerText);
         string ReturnIDOutput = datanode.SelectSingleNode("ReturnIDOutput").InnerText;

         //Assertion
         UserAlbums userAlbum = PSProvider.Instance.GetUserAblums(userId, FriendID);
         Assert.AreEqual(AlbumCounterExpected, userAlbum.AlbumCount >= 0, AlbumCounterOutput + userAlbum.AlbumCount);
         Assert.AreEqual(PhotoCounterExpected, userAlbum.PhotoCount >= 0, PhotoCounterOutput + userAlbum.PhotoCount);
         Assert.AreEqual(ReturnIDExpected, userAlbum.User.Id == userId, ReturnIDOutput + userAlbum.User.Id);
     }
 }

首先把XML文件load进XMLDOCUMENT对象中,然后用反射获取该方法的名字,因为方法名也是测试数据节点的名称。然后选中改方法的测试数据的节点。把所有的子结点放到一个XMLNODELIST对象里面,对这个对象进行枚举,然后就开始真正的工作了。如果isEnable是True,那么就读取该节点的下面的数据然后运行测试;如果为False就跳过该条测试数据,跳到下一条。

一个脚本对应一套测试数据,实际操作起来是很不好的,如果需要增加测试数据的话成本比较高,数据驱动的特点就是测试数据和脚本的分离,使得测试数据容易维护。其实测试重要的是数据而不是承载这些数据的脚本。应该把关注点放到设计一些好的测试数据上面。

Leave a Reply

Your email address will not be published. Required fields are marked *