成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

單元測(cè)試和集成測(cè)試業(yè)務(wù)應(yīng)用程序

Godtoy / 2704人閱讀

摘要:本文主要通過(guò)小例子介紹下單元測(cè)試,集成測(cè)試,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)等概念。在一個(gè)單元測(cè)試中,一次只能測(cè)試一個(gè)類(lèi)。在單元測(cè)試的基礎(chǔ)上,將所有模塊按照設(shè)計(jì)要求如根據(jù)結(jié)構(gòu)圖組裝成為子系統(tǒng)或系統(tǒng),進(jìn)行集成測(cè)試。

本文主要通過(guò)小例子介紹下單元測(cè)試,集成測(cè)試,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)等概念。

切入正文:

單元測(cè)試是通過(guò)寫(xiě)代碼來(lái)測(cè)試代碼的一個(gè)小單位測(cè)試方式。在一個(gè)單元測(cè)試中,一次只能測(cè)試一個(gè)類(lèi)。例如,如果你正在測(cè)試一個(gè)使用類(lèi)File,而另一個(gè)測(cè)試類(lèi)DummyFile是用來(lái)消除對(duì)物理文件系統(tǒng)的需要,單元測(cè)試的測(cè)試類(lèi)邏輯應(yīng)該被測(cè)試。為了使class實(shí)現(xiàn)從真實(shí)到虛/存根之間切換,接口代替真正的類(lèi)使用。所以,你的類(lèi)應(yīng)使用IFile或IDatabase而不是直接使用File和Database。

集成測(cè)試,也叫組裝測(cè)試或聯(lián)合測(cè)試。在單元測(cè)試的基礎(chǔ)上,將所有模塊按照設(shè)計(jì)要求(如根據(jù)結(jié)構(gòu)圖)組裝成為子系統(tǒng)或系統(tǒng),進(jìn)行集成測(cè)試。例如:正在測(cè)試CunsomerData,它依賴(lài)于LINQ to SQL數(shù)據(jù)庫(kù)連接,在集成測(cè)試中,確保能正常調(diào)動(dòng)所有相關(guān)類(lèi)的方法。

測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)是單元測(cè)試的極端形式??偟脑瓌t是先寫(xiě)單元測(cè)試,然后編寫(xiě)實(shí)際的代碼。例如,先寫(xiě)單元測(cè)試,測(cè)試CustomerData與在沒(méi)有真正的代碼類(lèi)CustomerData。該CustomerData類(lèi)可能包含類(lèi)似功能InsertCustomer ,DeleteCustomer , GetCustomer等,它們做的不外乎返回一些虛擬Customer對(duì)象,以滿(mǎn)足單元測(cè)試。 一旦單元測(cè)試都與虛擬數(shù)據(jù)相連,然后你開(kāi)始寫(xiě)的CustomerData實(shí)際代碼,它就會(huì)訪問(wèn)數(shù)據(jù)庫(kù)做真正的處理。編寫(xiě)真正的代碼后,單元測(cè)試并沒(méi)有改變測(cè)試代碼。TDD要求類(lèi)被設(shè)計(jì)在沒(méi)有直接依賴(lài)于其他類(lèi)中。所有的依賴(lài)關(guān)系通過(guò)接口。例如,CustomerData不直接使用SqlConnection,而是使用ISqlConnection ,所有的依賴(lài)提供給CustomerData構(gòu)造。

使用行為驅(qū)動(dòng)開(kāi)發(fā)測(cè)試

測(cè)試的單一方法用于單個(gè)期望是麻煩的。你必須寫(xiě)更多的測(cè)試方法來(lái)測(cè)試每個(gè)方法的整體行為。此外,在每個(gè)測(cè)試方法中,你必須在適當(dāng)環(huán)境下建立測(cè)試類(lèi),只是為了驗(yàn)證一個(gè)特定的期望方式。
例如:給定一個(gè)空Stack 當(dāng)一個(gè)項(xiàng)目被壓入堆棧和Pop被稱(chēng)為對(duì)象然后壓入堆棧中的最后一項(xiàng)被返回時(shí),該項(xiàng)目從堆棧中移除,以及任何額外調(diào)用Pop都拋出異常。這里定義完整行為的Pop方法。測(cè)試這種行為方法的所有預(yù)期和相關(guān)行為Pop 。

使用BDD單元測(cè)試

在第一個(gè)例子中,我們將進(jìn)行單元測(cè)試數(shù)據(jù)訪問(wèn)層。使用LINQ到SQL對(duì)象持久化數(shù)據(jù)訪問(wèn)層交易緩存在實(shí)體層面。例如,當(dāng)你要加載一個(gè)用戶(hù),它首先檢查高速緩存,看看用戶(hù)是否已經(jīng)緩存,如果沒(méi)有,它從數(shù)據(jù)庫(kù)中加載用戶(hù),然后緩存它。我們來(lái)看看PageRepository ,其中所涉及Page實(shí)體持久性。共同創(chuàng)建,讀取,更新和銷(xiāo)毀(CRUD)方法。舉一個(gè)例子方法GetPageById ,需要一個(gè)PageId并加載該P(yáng)age從數(shù)據(jù)庫(kù)中。

public class PageRepository : IPageRepository
{
    #region Fields
    private readonly IDropthingsDataContext _database;
    private readonly ICache _cacheResolver;
    #endregion Fields

    #region Constructors
    public PageRepository(IDropthingsDataContext database, ICache cacheResolver)
    {
        this._database = database;
        this._cacheResolver = cacheResolver;
    }
    #endregion Constructors

    #region Methods
    public Page GetPageById(int pageId)
    {
        string cacheKey = CacheSetup.CacheKeys.PageId(pageId);
        object cachedPage = _cacheResolver.Get(cacheKey);

        if (null == cachedPage)
        {
            var page = _database.GetSingle(
                   DropthingsDataContext.SubsystemEnum.Page, 
                    pageId, 
                    LinqQueries.CompiledQuery_GetPageById);
            page.Detach();
            _cacheResolver.Add(cacheKey, page);
            return page;
        }
        else
        {
            return cachedPage as Page;
        }
    } 
}

PageRepository需要IDropthingsDataContext ,這是測(cè)試與LINQ to SQL的一個(gè)單位DataContext 。默認(rèn)情況下,LINQ到SQL不會(huì)生成DataContext就是單元測(cè)試。你將不得不嘗試做一個(gè)DataContext單元測(cè)試。接著,它需要一個(gè)ICache其是與緩存涉及的接口。在這個(gè)例子中,假設(shè)有一個(gè)名為類(lèi)EnterpriseLibraryCache它將實(shí)現(xiàn)ICache 。

測(cè)試及預(yù)期結(jié)果確保:鑒于新PageRepository和一個(gè)空的緩存,當(dāng) GetPageById是帶一個(gè)PageId 。它首先檢查緩存,如果發(fā)現(xiàn)沒(méi)有,它從數(shù)據(jù)庫(kù)中的網(wǎng)頁(yè)中加載,并返回預(yù)期頁(yè)面。

public void GetPage_Should_Return_A_Page_from_database_when_cache_is_empty_and_then_caches_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", ...};
    database
        .Expect(d => d.GetSingle(
                              DropthingsDataContext.SubsystemEnum.Page,
                               1, LinqQueries.CompiledQuery_GetPageById))
        .Returns(samplePage);
    "Given PageRepository and empty cache".Context(() =>
        {
            // cache is empty
            cache.Expect(c => c.Get(It.IsAny())).Returns(default(object));
            // It will cache the Page object afte loading from database
            cache.Expect(c =>
                 c.Add(It.Is(cacheKey =>
                       cacheKey == CacheSetup.CacheKeys.PageId(pageId)), 
                      It.Is(cachePage =>
                          object.ReferenceEquals(cachePage, samplePage))))
                .AtMostOnce().Verifiable();
        });
    "when GetPageById is called".Do(() =>
        page = pageRepository.GetPageById(1));
    "it checks in the cache first and finds nothing and then caches it".Assert(() =>
        cache.VerifyAll());
    "it loads the page from database".Assert(() =>
        database.VerifyAll());
    "it returns the page as expected".Assert(() =>
        {
            Assert.Equal(pageId, page.ID);
        });  
}

單元測(cè)試的意義何在?

我覺(jué)得寫(xiě)單元測(cè)試時(shí),所測(cè)試的方法不只是在調(diào)用測(cè)試方法。單元測(cè)試已經(jīng)確切地知道什么其它的類(lèi)和方法將被調(diào)用。在上面的例子中,是否使用cache或database是在方法中決定的,所以,可以進(jìn)行邏輯測(cè)試。例如,我改變了代碼來(lái)使用AspectF庫(kù)。這需要代碼變更PageRepository 。更改代碼后,我需要確保PageRepository還是按照預(yù)期的行為。不管我用什么方法的緩存,它不應(yīng)該改變緩存行為:檢查緩存,以確保所請(qǐng)求的對(duì)象是不是已經(jīng)在緩存中,然后從數(shù)據(jù)庫(kù)中加載并緩存它。改變方法GetPageById,實(shí)施后AspectF ,如下所示:

public Page GetPageById(int pageId)
{
    return AspectF.Define
        .Cache(_cacheResolver, CacheSetup.CacheKeys.PageId(pageId))
        .Return(() =>
            _database.GetSingle(DropthingsDataContext.SubsystemEnum.Page,
                pageId, LinqQueries.CompiledQuery_GetPageById).Detach());
}

現(xiàn)在,當(dāng)我運(yùn)行單元測(cè)試,它表示通過(guò)。

它確認(rèn)行為PageRepository沒(méi)有改變,盡管它的代碼急劇變化。有了正確的單元測(cè)試,即使你在代碼中改變了,只要你的單元測(cè)試全部通過(guò),你的系統(tǒng)是沒(méi)有問(wèn)題。接下來(lái)讓我們來(lái)測(cè)試,當(dāng)緩存滿(mǎn)了,它正確地從緩存中返回一個(gè)對(duì)象,而不是不必要的查詢(xún)數(shù)據(jù)庫(kù)。下面的試驗(yàn)將確保:

[Specification]
public void GetPage_Should_Return_A_Page_from_cache_when_it_is_already_cached()
{
    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.Empty, VersionNo = 1,
            PageType = Enumerations.PageTypeEnum.PersonalPage,
            CreatedDate = DateTime.Now };
    "Given PageRepository and the requested page in cache".Context(() =>
    {
        cache.Expect(c => c.Get(CacheSetup.CacheKeys.PageId(samplePage.ID)))
            .Returns(samplePage);
    });
    "when GetPageById is called".Do(() =>
        page = pageRepository.GetPageById(1));            
    "it checks in the cache first and finds the object is in cache".Assert(() => 
    {
        cache.VerifyAll();
    });
    "it returns the page as expected".Assert(() =>
    {
        Assert.Equal(pageId, page.ID);
    });
}

這個(gè)試驗(yàn)是很簡(jiǎn)單的。唯一的區(qū)別是在設(shè)置Context ,我們?cè)O(shè)定一個(gè)期望,從緩存請(qǐng)求特定的頁(yè)面時(shí),它將返回samplePage對(duì)象。只要其中任何被調(diào)用函數(shù)中有沒(méi)有期望設(shè)置,Mock將拋出一個(gè)異常。如果代碼試圖調(diào)用任何database對(duì)象或任何東西上的其他cache對(duì)象時(shí),它會(huì)拋出一個(gè)異常,從而表明它沒(méi)有做什么不應(yīng)該做的。

集成測(cè)試使用BDD

集成測(cè)試意味著你要測(cè)試的一些類(lèi),它與其它類(lèi)和基礎(chǔ)設(shè)施集成,如數(shù)據(jù)庫(kù),文件系統(tǒng),郵件服務(wù)器等,當(dāng)你寫(xiě)一個(gè)集成測(cè)試,測(cè)試組件的行為應(yīng)該是沒(méi)有任何實(shí)物模型。此外,它們提供額外的信心代碼工作,因?yàn)樗斜匦璧慕M件和依賴(lài)關(guān)系也被測(cè)試。

如何測(cè)試業(yè)務(wù)外觀層,業(yè)務(wù)外觀處理數(shù)據(jù)訪問(wèn)組件和所有其他實(shí)用程序組件的編排。它封裝了用戶(hù)操作為一體的商業(yè)運(yùn)作。例如,在Dropthings ,當(dāng)?shù)谝淮稳碌挠脩?hù)訪問(wèn),用戶(hù)獲得創(chuàng)建默認(rèn)的頁(yè)面和窗口小部件。這些頁(yè)面和小部件來(lái)自一個(gè)模板。有一個(gè)名為anon_user@dropthings.com的用戶(hù)擁有默認(rèn)的頁(yè)面和窗口小部件。特定用戶(hù)的頁(yè)面和窗口小部件被復(fù)制到每一個(gè)新用戶(hù)中。由于這是一個(gè)復(fù)雜的操作,適合做自動(dòng)化的集成測(cè)試。

當(dāng)用戶(hù)首次訪問(wèn)該Default.aspx,該FirstVisitHomePage是呼吁Facade。它通過(guò)一個(gè)復(fù)雜的過(guò)程來(lái)克隆模板頁(yè)面、小部件和設(shè)置默認(rèn)用戶(hù)設(shè)置等集成測(cè)試,將確保如果FirstVisitHomePage被調(diào)用參數(shù)標(biāo)識(shí)一個(gè)新的用戶(hù)訪問(wèn)的站點(diǎn),那么它將返回可以對(duì)用戶(hù)創(chuàng)建的默認(rèn)頁(yè)面和部件的一個(gè)對(duì)象。因此: 由于之前從來(lái)沒(méi)有誰(shuí)訪問(wèn)過(guò)該網(wǎng)站的匿名用戶(hù), 當(dāng)用戶(hù)第一次訪問(wèn), 然后在準(zhǔn)確的列和位置作為anon_user的網(wǎng)頁(yè)新創(chuàng)建的頁(yè)面創(chuàng)建的小部件。

public class TestUserVisit
{
  public TestUserVisit()
  {
    Facade.BootStrap();
  }
  /// 
  /// Ensure the first visit produces the pages and widgets defined in the template user
  /// 
  [Specification]
  public void First_visit_should_create_same_pages_and_widgets_as_the_template_user()
  {
    MembershipHelper.UsingNewAnonUser((profile) =>
    {
      using (var facade = new Facade(new AppContext(string.Empty, profile.UserName)))
      {
        UserSetup userVisitModel = null;

        // Load the anonymous user pages and widgets
        string anonUserName = facade.GetUserSettingTemplate()
             .AnonUserSettingTemplate.UserName;
        var anonPages = facade.GetPagesOfUser(facade.GetUserGuidFromUserName(anonUserName));

        "Given anonymous user who has never visited the site before"
           .Context(() => { });

        "when the user visits for the first time".Do(() =>
        {
          userVisitModel = facade.FirstVisitHomePage(profile.UserName,
             string.Empty, true, false);
        });

        "it creates widgets on the newly created page at exact columns and
         positions as the anon user"s pages".Assert(() =>
        {
          anonPages.Each(anonPage =>
          {
            var userPage = userVisitModel.UserPages.First(page =>
                    page.Title == anonPage.Title
                    && page.OrderNo == anonPage.OrderNo
                    && page.PageType == anonPage.PageType);

            facade.GetColumnsInPage(anonPage.ID).Each(anonColumn =>
            {
              var userColumns = facade.GetColumnsInPage(userPage.ID);
              var userColumn = userColumns.First(column =>
                      column.ColumnNo == anonColumn.ColumnNo);
              var anonColumnWidgets = 
                facade.GetWidgetInstancesInZoneWithWidget(anonColumn.WidgetZoneId);
              var userColumnWidgets = 
                facade.GetWidgetInstancesInZoneWithWidget(userColumn.WidgetZoneId);
              // Ensure the widgets from the anonymous user template"s columns are 
              // in the same column and row.
              anonColumnWidgets.Each(anonWidget =>
                 Assert.True(userColumnWidgets.Where(userWidget =>
                  userWidget.Title == anonWidget.Title
                  && userWidget.Expanded == anonWidget.Expanded
                  && userWidget.State == anonWidget.State
                  && userWidget.Resized == anonWidget.Resized
                  && userWidget.Height == anonWidget.Height
                  && userWidget.OrderNo == anonWidget.OrderNo).Count() == 1));
            });
          });
        });
      }
    });
  }

需要進(jìn)一步的解釋?zhuān)簽閺哪0逵脩?hù)發(fā)現(xiàn)每個(gè)頁(yè)面確保新用戶(hù)從模板用戶(hù)的頁(yè)面的部件獲得完全一樣的頁(yè)面。獲得來(lái)自新用戶(hù)的頁(yè)面的窗口小部件比較每個(gè)插件。當(dāng)在做業(yè)務(wù)層的變化對(duì)于每個(gè)插件確保具有相同的名稱(chēng),狀態(tài),位置等獨(dú)一無(wú)二的部件,我可以運(yùn)行集成測(cè)試,以確保關(guān)鍵功能是否按預(yù)期工作完成,而且在整個(gè)業(yè)務(wù)層沒(méi)有破損任何地方。 我用xunit.console.exe上運(yùn)行的集成測(cè)試測(cè)試并生成一個(gè)不錯(cuò)html報(bào)告:

該報(bào)告使用下面的命令行產(chǎn)生:
d:xunitxunit.console.exe
d:trunksrcDropthings.Business.Facade.TestsbinDebugDropthings.Business.Facade.Tests.dll

/html FacadeTest.html

您可以使用GUI xUnit:

使用BDD的單元測(cè)試測(cè)試驅(qū)動(dòng)開(kāi)發(fā)
到目前為止,我們已經(jīng)通過(guò)代碼編寫(xiě)測(cè)試,但如果你先代碼編寫(xiě)測(cè)試有關(guān)驅(qū)動(dòng)開(kāi)發(fā)?假設(shè)我們要添加行為:給定一個(gè)PageRepository ,當(dāng) Insert被調(diào)用時(shí),它應(yīng)該在數(shù)據(jù)庫(kù)中插入頁(yè)面,清除了得到的新頁(yè)面,用戶(hù)頁(yè)面任何緩存集合,返回新插入的頁(yè)面。
編寫(xiě)測(cè)試代碼:

[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);
  });      
}

首先,我們將寫(xiě)一些虛擬代碼PageRepository.Insert方法,返回一個(gè)新的Page。它應(yīng)該會(huì)fail,因?yàn)樗粷M(mǎn)足目前數(shù)據(jù)庫(kù)對(duì)象的期望集。如果沒(méi)有失敗,則表明我們的測(cè)試是錯(cuò)誤的。

public Page Insert(Action populate)
        {
                    return new Page();
        } 

運(yùn)行故障測(cè)試結(jié)果如預(yù)期:

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.
b__34()

這表明,沒(méi)有呼叫database.Insert ,所以測(cè)試失敗。我們實(shí)現(xiàn)了TDD的第一步,這是寫(xiě)一個(gè)測(cè)試并使其失敗以來(lái)的第一期望沒(méi)有正確組件下檢驗(yàn)。
現(xiàn)在添加真正的代碼:

public Page Insert(Action populate)
       {
           var newPage = _database.Insert(
               DropthingsDataContext.SubsystemEnum.Page, populate);
           RemoveUserPagesCollection(newPage.UserId);
           return newPage.Detach();
       }

現(xiàn)在,當(dāng)單元測(cè)試是對(duì)的PageRepository ,它通過(guò)新的測(cè)試,與以往的測(cè)試一起:

使用BDD進(jìn)行集成測(cè)試測(cè)試驅(qū)動(dòng)開(kāi)發(fā)

如果我們想為集成測(cè)試做TDD?我們?nèi)绾蜗葘?xiě)測(cè)試代碼,然后寫(xiě)它與其他組件集成業(yè)務(wù)層的代碼? 我們?nèi)绾螢閃eb層做TDD?方法是一樣的,首先你編寫(xiě)測(cè)試代碼,給出正確的輸入,并期望從中活得輸出,然而,集成測(cè)試不應(yīng)該只調(diào)用多帶帶一個(gè)業(yè)務(wù)操作,以確保它能正常工作。集成測(cè)試還應(yīng)該確保執(zhí)行其它操作時(shí)出現(xiàn)正確的行為。例如,在測(cè)試FirstVisitHomePage 時(shí),期望的是,第一次訪問(wèn)之后,用戶(hù)具有創(chuàng)建的正確頁(yè)面。測(cè)試代碼通過(guò)檢查返回的對(duì)象模型驗(yàn)證這一點(diǎn),但實(shí)際情況是,在第一次訪問(wèn)后,根據(jù)用戶(hù)的返回,他們應(yīng)該看到相同的部件,再次確認(rèn)第一和復(fù)診返回相同的數(shù)據(jù)。

測(cè)試如下:

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));
          });
        });
      });
    }
  });
}

做集成測(cè)試的正確方法是編寫(xiě)單元測(cè)試的對(duì)立面。在單元測(cè)試中,這種方法是通過(guò)調(diào)用一種方法和存根。在集成測(cè)試,你應(yīng)該測(cè)試不僅只有一個(gè)操作,而且還執(zhí)行其它相關(guān)操作,以確保測(cè)試的操作確實(shí)是它應(yīng)該做的。概括了可能的測(cè)試用例分為以下類(lèi)別:

當(dāng)測(cè)試創(chuàng)建新數(shù)據(jù)操作(例如,在數(shù)據(jù)庫(kù)中插入行或調(diào)用Web服務(wù)來(lái)創(chuàng)建一個(gè)實(shí)體),保證了操作通過(guò)適當(dāng)進(jìn)行:

  調(diào)用,通過(guò)再次讀取該行或調(diào)用另一個(gè)Web服務(wù),以獲得創(chuàng)建的實(shí)體讀取數(shù)據(jù)等操作。如果數(shù)據(jù)沒(méi)有被正確插入(例如,插入子行)應(yīng)該失敗。 這是一個(gè)積極的測(cè)試。        
  調(diào)用如果插入成功,例如再次插入同一行會(huì)產(chǎn)生一個(gè)違反約束,將失敗的其它操作。這是一種消極的考驗(yàn)。

當(dāng)測(cè)試的操作的更新數(shù)據(jù)(例如,更新數(shù)據(jù)庫(kù)中的行),保證了操作的數(shù)據(jù),通過(guò)適當(dāng)更新

  調(diào)用使用更新后的數(shù)據(jù),如果沒(méi)有正確的更新數(shù)據(jù)會(huì)失敗,例如其它的操作使余額不足的賬戶(hù)兩次連續(xù)的匯款后。這是一個(gè)積極的測(cè)試。
  如果調(diào)用更新成功,將是失敗的其它操作,例如嘗試使用更新后的相同值約束沖突在數(shù)據(jù)庫(kù)中插入新行。這是一種消極的考驗(yàn)。

當(dāng)你測(cè)試刪除一些數(shù)據(jù)的操作,保證適當(dāng)?shù)臄?shù)據(jù)刪除

  如果調(diào)用數(shù)據(jù)存在例如重新插入同一行產(chǎn)生一個(gè)違反約束將失敗,其它的操作。
  呼叫,如果數(shù)據(jù)被正確地刪除,例如插入的子行是不存在的行,將是失敗的操作。

在集成測(cè)試做正反兩方面的測(cè)試,即使你正在做單元測(cè)試,以確保測(cè)試涵蓋了系統(tǒng)的所有主要行為是很重要的。集成測(cè)試的一個(gè)好處是在假設(shè)你自己的代碼已經(jīng)單元測(cè)試的基礎(chǔ)設(shè)施不可預(yù)測(cè)性超過(guò)測(cè)試自己的代碼。重要的是盡可能多的從正和負(fù)兩方面盡可能覆蓋以排除基礎(chǔ)設(shè)施變量。

作者信息:
原文作者:Omar Al Zabir
原文鏈接:http://www.codeproject.com/Articles/44276/Unit-Testing-and-Integration-Testing-in-Business-A
翻譯來(lái)與Maxleap團(tuán)隊(duì)_云服務(wù)研發(fā)成員:Sunny Zhang
中文翻譯原鏈接:https://blog.maxleap.cn/archives/855

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/8727.html

相關(guān)文章

  • 隨行付微服務(wù)測(cè)試單元測(cè)試

    摘要:輸出結(jié)果需要人工檢查的測(cè)試不是一個(gè)好的單元測(cè)試。為了有效的進(jìn)行單元測(cè)試,需要遵循一定的方法,通常采用路徑覆蓋法設(shè)計(jì)單元測(cè)試用例。 在微服務(wù)架構(gòu)下高覆蓋率的單元測(cè)試是保障代碼質(zhì)量的第一道也是最重要的關(guān)口,應(yīng)該持之以恒。 背景 單元測(cè)試為代碼質(zhì)量保駕護(hù)航,是提高業(yè)務(wù)質(zhì)量的最直接手段,實(shí)踐證明,非常多的缺陷完全可以通過(guò)單元測(cè)試來(lái)發(fā)現(xiàn),測(cè)試金字塔提出者M(jìn)artin Fowler 強(qiáng)調(diào)如果一個(gè)高...

    xiguadada 評(píng)論0 收藏0
  • 詳解Gradle自動(dòng)實(shí)現(xiàn)Android組件化

    摘要:我們一般把自動(dòng)化測(cè)試劃分為三種分別是單元測(cè)試目的是測(cè)試代碼的最小單元。集成測(cè)試用來(lái)測(cè)試一個(gè)完成的組件或子系統(tǒng),確保多個(gè)類(lèi)之間的交互是否按預(yù)期運(yùn)行。集成測(cè)試需要比單元測(cè)試需要更長(zhǎng)的執(zhí)行時(shí)間,而且更加難以維護(hù),失敗的原因難以診斷。 前言;為什么我們要用Gradle管理組件呢?先來(lái)看看Android組件化需要實(shí)現(xiàn)的目標(biāo)按照業(yè)務(wù)邏輯劃分模塊項(xiàng)目模塊能夠單獨(dú)啟動(dòng)測(cè)試能夠根據(jù)需求引入或刪除某些業(yè)務(wù)模塊通...

    番茄西紅柿 評(píng)論0 收藏0
  • 再談自動(dòng)化測(cè)試——我們?cè)诰帉?xiě)測(cè)試時(shí),應(yīng)該注意什么

    摘要:原則具體包括自動(dòng)化獨(dú)立性可重復(fù)簡(jiǎn)單的解釋一下三個(gè)原則單元測(cè)試應(yīng)該是全自動(dòng)執(zhí)行的。為了保證單元測(cè)試穩(wěn)定可靠且便于維護(hù),需要保證其獨(dú)立性。原則編寫(xiě)單元測(cè)試用例時(shí)為了保證被測(cè)模塊的交付質(zhì)量需要符合原則。與設(shè)計(jì)文檔相結(jié)合來(lái)編寫(xiě)單元測(cè)試。 本文首發(fā)于泊浮目的專(zhuān)欄:https://segmentfault.com/blog... 背景 最近項(xiàng)目在測(cè)試階段陸陸續(xù)續(xù)的測(cè)出了一些bug.這個(gè)情況剛出現(xiàn)...

    My_Oh_My 評(píng)論0 收藏0
  • 前端進(jìn)階之路: 前端架構(gòu)設(shè)計(jì)(3) - 測(cè)試核心

    摘要:而測(cè)試驅(qū)動(dòng)開(kāi)發(fā)技術(shù)并不只是單純的測(cè)試工作。需求向來(lái)就是軟件開(kāi)發(fā)過(guò)程中感覺(jué)最不好明確描述易變的東西。這里說(shuō)的需求不只是指用戶(hù)的需求,還包括對(duì)代碼 可能很多人和我一樣, 首次聽(tīng)到前端架構(gòu)這個(gè)詞, 第一反應(yīng)是: 前端還有架構(gòu)這一說(shuō)呢? 在后端開(kāi)發(fā)領(lǐng)域, 系統(tǒng)規(guī)劃和可擴(kuò)展性非常關(guān)鍵, 因此架構(gòu)師備受重視, 早在開(kāi)發(fā)工作啟動(dòng)之前, 他們就被邀請(qǐng)加入到項(xiàng)目中, 而且他們會(huì)跟客戶(hù)討論即將建成的平臺(tái)的...

    Karuru 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<