在单元测试中应用数据驱动
作为自动化测试的一种脚本技术,数据驱动技术在10几年前已经有人提出,并且沿用到现在。Nunit作为C#开发下的一种常用的单元测试框架,也是被广泛使用。但是对于单元测试,很多时候都是硬编码,虽然说用Nunit编写单元测试代码是挺廉价的,但是为什么不让自己的工作更加轻松,脚本更加灵活呢?
我把单元测试的数据放到单独的XML文件里面,测试运行的时候就读取XML数据然后进行测试。由于我想做一个轻量级的单元测试,所以并没有把很多判断控制的语句写到一个单元测试里面,而是把有不同行为的测试写到一个脚本里面。例如期望结果都是成功的一类测试就写到一个脚本,包含同样错误的测试就写到另外一个脚本,尽管这样看起来并不是很高明,但是对于我的工作来说这是足够的了。
首先是XML文件的结构,其实很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?xml version="1.0" encoding="utf-8" ?> <TestData> <TestXMLD> <TestCase_1 isEnable="True"> <UserID>111</UserID> <FriendID>222</FriendID> <AlbumCounterExpected>True</AlbumCounterExpected> <AlbumCounterOutput>User album counter should greater equal than 0, the actual value is </AlbumCounterOutput> <PhotoCounterExpected>True</PhotoCounterExpected> <PhotoCounterOutput>User photo counter should greater equal than 0, the actual value is </PhotoCounterOutput> <ReturnIDExpected>True</ReturnIDExpected> <ReturnIDOutput>Return user ID is not valid, the actual value is </ReturnIDOutput> </TestCase_1> <TestCase_2 isEnable="True"> <UserID>222</UserID> <FriendID>111</FriendID> <AlbumCounterExpected>True</AlbumCounterExpected> <AlbumCounterOutput>User album counter should greater equal than 0, the actual value is </AlbumCounterOutput> <PhotoCounterExpected>True</PhotoCounterExpected> <PhotoCounterOutput>User photo counter should greater equal than 0, the actual value is </PhotoCounterOutput> <ReturnIDExpected>True</ReturnIDExpected> <ReturnIDOutput>Return user ID is not valid, the actual value is </ReturnIDOutput> </TestCase_2> </TestXMLD> </TestData> |
根节点是TestData,它的子结点是TestXMLD,这个节点是跟我的单元测试方法同名的。这个节点下面有2个子结点,分别是两套测试的数据,每套测试的数据分别包含了测试运行时候用到的数据,期望结果以及测试不能通过时候所要显示的错误提示。而且每套数据都包含一个isEnable的属性,如果设为False,那么测试运行的时候就不跑这一条数据。
测试的方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | [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就跳过该条测试数据,跳到下一条。
一个脚本对应一套测试数据,实际操作起来是很不好的,如果需要增加测试数据的话成本比较高,数据驱动的特点就是测试数据和脚本的分离,使得测试数据容易维护。其实测试重要的是数据而不是承载这些数据的脚本。应该把关注点放到设计一些好的测试数据上面。
Related posts:

Leave a Reply