用Visual Studio Team版进行测试驱动开发
【IT168 专稿】
自由地编写代码,或是不按着标准来写程序可能会更快地完成一个系统,但是这样的系统也会经常给我们带来恶梦。那么这个问题的解决方案就是就是进行适当的代码测试来引导我们的代码走向正轨。测试驱动开发(TDD)将会通过预定义的测试规则简化开发和减少程序中的bug。最初,进行TDD是十分困难的,但当这种技术获得团队的认可的同时,也获得了很多开发工具的支持。在本文中我们将学习如何使用Visual Studio Team版本来进行测试和实现单元测试和web测试。
一、介绍
我们可以从MSDN中学习到更多的关于TDD的知识,但如果用一句话来描述TDD,它就是从测试中驱动开发。因此,我们需要首先在写正式的代码之前要写测试代码,然后使用这些测试来确保我们的代码满足需求。例如,假设我们想写一个计算阶乘的方法。首先,我们需要写一个测试确定从方法中返回的值是不是正确。如下面的测试代码如示:
public void TestFactorial() { int expected = 6; int actual = MyClass.CalculateFactorial(3); Assert.AreEqual(expected, actual, "Factorial is Invalid"); expected = 1; actual = MyClass.CalculateFactorial(0); Assert.AreEqual(expected, actual, "Factorial is Invalid"); }
Assert是一个静态类,在Microsoft.VisualStudio.TestTools.UnitTesting命名空间中可以找到它。我们可以使用Assert.AreEqual方法来测试两个值是否相等。当这两个值不等时,返回false。下面的计算阶乘的代码:
class MyClass { public static int CalculateFactorial(int Number) { if (Number < 0) return -1; // -1 = undefined if (Number <= 1) return 1; int fact = 1; for (int i = 0; i < Number; i++) { fact = fact * (Number - i); } return fact; } }
顺序测试:这个测试类型由一系列按指定顺序排列的测试组成。要注意的是我们不能将负载测试包括在里面。
现在我们已经有了对TDD的基本理解,下面我们可以来探索VSTET在TDD中的使用。在VSTET中为我们提供了用于测试的每一样东西。在VSTET中允许我们建立、编辑和运行测试。我们甚至可以存储测试结果。
单元测试:正如名子所示,这个测试类型的目标是测试我们的单元代码。VSTET集成了用于单元测试的框架:Microsoft.VisualStudio.QualityTools.UnitTestFramework,这个框架包括了各种命用于编写、执行、报告单元测试的名空间、类以及接口。
Web测试:这个测试类型可以让我们记录Web应用程序页面之间的交互和导航动作。我们可以多次使用这些动作序列来测试我们的应用程序。
普通测试:这种测试类型可以让我们使用第三方的测试工具。例如,我们可以从其他测试源或在VSTET中可运行的测试导入到VSTET中。在我们包装了我们已经存在的测试、可执行的测试或第三方的工具后,VSTET就会象对待自己的测试一样对待它们。
为了学习如何在VSTET中使用不同的测试类型来工具,本文使用了一个可下载的网站的例子,这个例子要求用户注册和登录。我们可以看看如图1的结构和组件。

[TestClass] public class UserDALTests { public UserDALTests() { // // TODO: Add constructor logic here // } private const string Email = "test@test.com"; [ClassInitialize()] public static void SetUpTestUser(TestContext testContext) { try { User user = new User(); user.Name = "Test"; user.Password="test@1"; user.Email = Email; UserDAL userDAL = new UserDAL(); userDAL.CreateUser(user); } catch (Exception ex) { Assert.Fail(ex.Message); } } [TestMethod] public void TestCreateUser() { try { User user = new User(); user.Name = "Test"; user.Password = "test@1"; user.Email = Email; UserDAL userDAL = new UserDAL(); userDAL.CreateUser(user); user = userDAL.GetUser(Email); Assert.AreEqual("Test", user.Name); Assert.AreEqual("test@1", user.Password); Assert.AreEqual(Email , user.Email); } catch (Exception ex) { Assert.Fail(ex.Message); } } [TestMethod] public void TestGetUser() { try { UserDAL userDAL = new UserDAL(); User user = userDAL.GetUser(Email); Assert.IsNotNull(user); Assert.AreEqual(Email, user.Email); } catch (Exception ex) { Assert.Fail(ex.Message); } } [TestMethod] public void TestGetUsers() { try { UserDAL userDAL = new UserDAL(); List<User> users = userDAL.GetUsers("(Email LIKE '%Test%')"); Assert.IsTrue(users.Count > 0); foreach (User user in users) { Assert.IsTrue(user.Email != null && user.Email != ""); } } catch (Exception ex) { Assert.Fail(ex.Message); } } [TestMethod] public void TestUpdateUser() { try { UserDAL userDAL = new UserDAL(); User user = userDAL.GetUser(Email); user.Name = "Update"; user.Password = "Update"; userDAL.UpdateUser(user); user = userDAL.GetUser(Email); Assert.IsNotNull(user); Assert.AreEqual(Email, user.Email); Assert.AreEqual("Update", user.Password); Assert.AreEqual("Update", user.Name); } catch (Exception ex) { Assert.Fail(ex.Message); } } [TestMethod] public void TestDeleteUser() { try { UserDAL userDAL = new UserDAL(); userDAL.DeleteUser(Email); User user = userDAL.GetUser(Email); Assert.IsNull(user); } catch (Exception ex) { Assert.Fail(ex.Message); } } [ClassCleanup] public static void ClearTestUser(TestContext testContext) { try { UserDAL userDAL = new UserDAL(); userDAL.DeleteUser(Email); } catch (Exception ex) { Assert.Fail(ex.Message); } } } }
[ClassInitialize()] public static void SetUpTestUser(TestContext testContext) { try { User user = new User(); user.Name = "Test"; user.Password="test@1"; user.Email = Email; UserDAL userDAL = new UserDAL(); userDAL.CreateUser(user); } catch (Exception ex) { Assert.Fail(ex.Message); } }
当我们手工设计自己的单元测试类时,初始化和清楚单元测试所需要的资源是非常容易的。例如,如果我们正在写数据库操作的单元测试,我们可以在被标识[ClassInitialize]属性的方法中建立一个模拟记录。并在类方法中来测试数据库操作,然后在标识为[ClassCleanup]属性的方法中删除这条记录。
一个Web应用程序是由请求、交互和响应混合而成的应用。因此,为了更好的测试它,我们需要模拟请求、交互和响应的动作。Web Tests允许我们记录请求、用户交互和在页面之彰的导航。可记录的,能重现的Web测试可以节省我们很多的时间。

Web测试的结果如图3所示。这个测试结果包含了三个HTTP响应。图3的响应信息面板显示了这些内容。如状态、响应时间和每一个请求的大小。

图3 Web工程测试结果