使用BDD的单元测试测试驱动开发 到目前为止,我们已经通过代码编写测试,但如果你先代码编写测试有关驱动开发?假设我们要添加行为:给定一个PageRepository ,当 Insert被调用时,它应该在数据库中插入页面,清除了得到的新页面,用户页面任何缓存集合,返回新插入的页面。 编写测试代码:
  [Specification]
  public void InsertPage_should_insert_a_page_in_database_and_cache_it()
  {
  var cache = new Mock();
  var database = new Mock();
  IPageRepository pageRepository = new PageRepository(database.Object, cache.Object);
  const int pageId = 1;
  var page = default(Page);
  var samplePage = new Page() { ID = pageId, Title = "Test Page", ColumnCount = 3,
  LayoutType = 3, UserId = Guid.NewGuid(), VersionNo = 1,
  PageType = Enumerations.PageTypeEnum.PersonalPage, CreatedDate = DateTime.Now };
  database
  .Expect(d => d.Insert(DropthingsDataContext.SubsystemEnum.Page,
  It.IsAny()))
  .Returns(samplePage);
  "Given PageRepository".Context(() =>
  {
  // It will clear items from cache
  cache.Expect(c => c.Remove(CacheSetup.CacheKeys.PagesOfUser(samplePage.UserId)));
  });
  "when Insert is called".Do(() =>
  page = pageRepository.Insert((newPage) =>
  {
  newPage.Title = samplePage.Title;
  newPage.ColumnCount = samplePage.ColumnCount;
  newPage.LayoutType = samplePage.LayoutType;
  newPage.UserId = samplePage.UserId;
  newPage.VersionNo = samplePage.VersionNo;
  newPage.PageType = samplePage.PageType;
  }));
  ("then it should insert the page in database" +
  "and clear any cached collection of pages for the user who gets the new page" +
  "and it returns the newly inserted page").Assert(() =>
  {
  database.VerifyAll();
  cache.VerifyAll();
  Assert.Equal(pageId, page.ID);
  });     
  }
  首先,我们将写一些虚拟代码PageRepository.Insert方法,返回一个新的Page。它应该会fail,因为它不满足目前数据库对象的期望集。如果没有失败,则表明我们的测试是错误的。
  public Page Insert(Action populate)
  {
  return new Page();
  }
  运行故障测试结果如预期:
  TestCase 'Given PageRepository when InsertPage is called, then it should insert the page in databaseand clear any cached collection of pages for the user who gets the new pageand it returns the newly inserted page' failed: Moq.MockVerificationException : The following expectations were not met: IDropthingsDataContext d => d.Insert(Page, null) at Moq.Mock`1.VerifyAll() PageRepositoryTest.cs(278,0): at Dropthings.DataAccess.UnitTest.PageRepositoryTest.<>c_DisplayClass35. <InsertPage_should_inserta_page_in_database_and_cache_it>b__34()
  这表明,没有呼叫database.Insert ,所以测试失败。我们实现了TDD的第一步,这是写一个测试并使其失败以来的第一期望没有正确组件下检验。 现在添加真正的代码:
  public Page Insert(Action populate)
  {
  var newPage = _database.Insert(
  DropthingsDataContext.SubsystemEnum.Page, populate);
  RemoveUserPagesCollection(newPage.UserId);
  return newPage.Detach();
  }
  现在,当单元测试是对的PageRepository ,它通过新的测试,与以往的测试一起:

  使用BDD进行集成测试测试驱动开发
  如果我们想为集成测试做TDD?我们如何先写测试代码,然后写它与其他组件集成业务层的代码? 我们如何为Web层做TDD?方法是一样的,首先你编写测试代码,给出正确的输入,并期望从中活得输出,然而,集成测试不应该只调用单独一个业务操作,以确保它能正常工作。集成测试还应该确保执行其它操作时出现正确的行为。例如,在测试FirstVisitHomePage 时,期望的是,第一次访问之后,用户具有创建的正确页面。测试代码通过检查返回的对象模型验证这一点,但实际情况是,在第一次访问后,根据用户的返回,他们应该看到相同的部件,再次确认第一和复诊返回相同的数据。
  测试如下:
  public void Revisit_should_load_the_pages_and_widgets_exactly_the_same()
  {
  MembershipHelper.UsingNewAnonUser((profile) =>
  {
  using (var facade = new Facade(new AppContext(string.Empty, profile.UserName)))
  {
  UserSetup userVisitModel = null;
  UserSetup userRevisitModel = null;
  "Given an anonymous user who visited first".Context(() =>
  {
  userVisitModel = facade.FirstVisitHomePage(profile.UserName, ...);
  });
  "when the same user visits again".Do(() =>
  {
  userRevisitModel = facade.RepeatVisitHomePage(profile.UserName, ...);
  });
  "it should load the exact same pages, column and
  widgets as the first visit produced".Assert(() =>
  {
  userVisitModel.UserPages.Each(firstVisitPage =>
  {
  Assert.True(userRevisitModel.UserPages.Exists(page =>
  page.ID == firstVisitPage.ID));
  var revisitPage = userRevisitModel.UserPages.First(page =>
  page.ID == firstVisitPage.ID);
  var revisitPageColumns = facade.GetColumnsInPage(revisitPage.ID);
  facade.GetColumnsInPage(firstVisitPage.ID).Each(firstVisitColumn =>
  {
  var revisitColumn = revisitPageColumns.First(column =>
  column.ID == firstVisitColumn.ID);
  var firstVisitWidgets = facade
  .GetWidgetInstancesInZoneWithWidget(firstVisitColumn.WidgetZoneId);
  var revisitWidgets = facade
  .GetWidgetInstancesInZoneWithWidget(revisitColumn.WidgetZoneId);
  firstVisitWidgets.Each(firstVisitWidget =>
  Assert.True(revisitWidgets.Where(revisitWidget =>
  revisitWidget.Id == firstVisitWidget.Id).Count() == 1));
  });
  });
  });
  }
  });
  }
  做集成测试的正确方法是编写单元测试的对立面。在单元测试中,这种方法是通过调用一种方法和存根。在集成测试,你应该测试不仅只有一个操作,而且还执行其它相关操作,以确保测试的操作确实是它应该做的。概括了可能的测试用例分为以下类别:
  当测试创建新数据操作(例如,在数据库中插入行或调用Web服务来创建一个实体),保证了操作通过适当进行: 调用,通过再次读取该行或调用另一个Web服务,以获得创建的实体读取数据等操作。如果数据没有被正确插入(例如,插入子行)应该失败。 这是一个积极的测试。
  调用如果插入成功,例如再次插入同一行会产生一个违反约束,将失败的其它操作。这是一种消极的考验。
  当测试的操作的更新数据(例如,更新数据库中的行),保证了操作的数据,通过适当更新 调用使用更新后的数据,如果没有正确的更新数据会失败,例如其它的操作使余额不足的账户两次连续的汇款后。这是一个积极的测试。 如果调用更新成功,将是失败的其它操作,例如尝试使用更新后的相同值约束冲突在数据库中插入新行。这是一种消极的考验。
  当你测试删除一些数据的操作,保证适当的数据删除 如果调用数据存在例如重新插入同一行产生一个违反约束将失败,其它的操作。 呼叫,如果数据被正确地删除,例如插入的子行是不存在的行,将是失败的操作。
  在集成测试做正反两方面的测试,即使你正在做单元测试,以确保测试涵盖了系统的所有主要行为是很重要的。集成测试的一个好处是在假设你自己的代码已经单元测试的基础设施不可预测性超过测试自己的代码。重要的是尽可能多的从正和负两方面尽可能覆盖以排除基础设施变量。
  作者信息:
  原文作者:Omar Al Zabir
  原文链接:http://www.codeproject.com/Articles/44276/Unit-Testing-and-Integration-Testing-in-Business-A