国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

單元測試和集成測試業務應用程序

Godtoy / 2641人閱讀

摘要:本文主要通過小例子介紹下單元測試,集成測試,測試驅動開發等概念。在一個單元測試中,一次只能測試一個類。在單元測試的基礎上,將所有模塊按照設計要求如根據結構圖組裝成為子系統或系統,進行集成測試。

本文主要通過小例子介紹下單元測試,集成測試,測試驅動開發等概念。

切入正文:

單元測試是通過寫代碼來測試代碼的一個小單位測試方式。在一個單元測試中,一次只能測試一個類。例如,如果你正在測試一個使用類File,而另一個測試類DummyFile是用來消除對物理文件系統的需要,單元測試的測試類邏輯應該被測試。為了使class實現從真實到虛/存根之間切換,接口代替真正的類使用。所以,你的類應使用IFile或IDatabase而不是直接使用File和Database。

集成測試,也叫組裝測試或聯合測試。在單元測試的基礎上,將所有模塊按照設計要求(如根據結構圖)組裝成為子系統或系統,進行集成測試。例如:正在測試CunsomerData,它依賴于LINQ to SQL數據庫連接,在集成測試中,確保能正常調動所有相關類的方法。

測試驅動開發(TDD)是單元測試的極端形式。總的原則是先寫單元測試,然后編寫實際的代碼。例如,先寫單元測試,測試CustomerData與在沒有真正的代碼類CustomerData。該CustomerData類可能包含類似功能InsertCustomer ,DeleteCustomer , GetCustomer等,它們做的不外乎返回一些虛擬Customer對象,以滿足單元測試。 一旦單元測試都與虛擬數據相連,然后你開始寫的CustomerData實際代碼,它就會訪問數據庫做真正的處理。編寫真正的代碼后,單元測試并沒有改變測試代碼。TDD要求類被設計在沒有直接依賴于其他類中。所有的依賴關系通過接口。例如,CustomerData不直接使用SqlConnection,而是使用ISqlConnection ,所有的依賴提供給CustomerData構造。

使用行為驅動開發測試

測試的單一方法用于單個期望是麻煩的。你必須寫更多的測試方法來測試每個方法的整體行為。此外,在每個測試方法中,你必須在適當環境下建立測試類,只是為了驗證一個特定的期望方式。
例如:給定一個空Stack 當一個項目被壓入堆棧和Pop被稱為對象然后壓入堆棧中的最后一項被返回時,該項目從堆棧中移除,以及任何額外調用Pop都拋出異常。這里定義完整行為的Pop方法。測試這種行為方法的所有預期和相關行為Pop 。

使用BDD單元測試

在第一個例子中,我們將進行單元測試數據訪問層。使用LINQ到SQL對象持久化數據訪問層交易緩存在實體層面。例如,當你要加載一個用戶,它首先檢查高速緩存,看看用戶是否已經緩存,如果沒有,它從數據庫中加載用戶,然后緩存它。我們來看看PageRepository ,其中所涉及Page實體持久性。共同創建,讀取,更新和銷毀(CRUD)方法。舉一個例子方法GetPageById ,需要一個PageId并加載該Page從數據庫中。

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 ,這是測試與LINQ to SQL的一個單位DataContext 。默認情況下,LINQ到SQL不會生成DataContext就是單元測試。你將不得不嘗試做一個DataContext單元測試。接著,它需要一個ICache其是與緩存涉及的接口。在這個例子中,假設有一個名為類EnterpriseLibraryCache它將實現ICache 。

測試及預期結果確保:鑒于新PageRepository和一個空的緩存,當 GetPageById是帶一個PageId 。它首先檢查緩存,如果發現沒有,它從數據庫中的網頁中加載,并返回預期頁面。

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

單元測試的意義何在?

我覺得寫單元測試時,所測試的方法不只是在調用測試方法。單元測試已經確切地知道什么其它的類和方法將被調用。在上面的例子中,是否使用cache或database是在方法中決定的,所以,可以進行邏輯測試。例如,我改變了代碼來使用AspectF庫。這需要代碼變更PageRepository 。更改代碼后,我需要確保PageRepository還是按照預期的行為。不管我用什么方法的緩存,它不應該改變緩存行為:檢查緩存,以確保所請求的對象是不是已經在緩存中,然后從數據庫中加載并緩存它。改變方法GetPageById,實施后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());
}

現在,當我運行單元測試,它表示通過。

它確認行為PageRepository沒有改變,盡管它的代碼急劇變化。有了正確的單元測試,即使你在代碼中改變了,只要你的單元測試全部通過,你的系統是沒有問題。接下來讓我們來測試,當緩存滿了,它正確地從緩存中返回一個對象,而不是不必要的查詢數據庫。下面的試驗將確保:

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

這個試驗是很簡單的。唯一的區別是在設置Context ,我們設定一個期望,從緩存請求特定的頁面時,它將返回samplePage對象。只要其中任何被調用函數中有沒有期望設置,Mock將拋出一個異常。如果代碼試圖調用任何database對象或任何東西上的其他cache對象時,它會拋出一個異常,從而表明它沒有做什么不應該做的。

集成測試使用BDD

集成測試意味著你要測試的一些類,它與其它類和基礎設施集成,如數據庫,文件系統,郵件服務器等,當你寫一個集成測試,測試組件的行為應該是沒有任何實物模型。此外,它們提供額外的信心代碼工作,因為所有必需的組件和依賴關系也被測試。

如何測試業務外觀層,業務外觀處理數據訪問組件和所有其他實用程序組件的編排。它封裝了用戶操作為一體的商業運作。例如,在Dropthings ,當第一次全新的用戶訪問,用戶獲得創建默認的頁面和窗口小部件。這些頁面和小部件來自一個模板。有一個名為anon_user@dropthings.com的用戶擁有默認的頁面和窗口小部件。特定用戶的頁面和窗口小部件被復制到每一個新用戶中。由于這是一個復雜的操作,適合做自動化的集成測試。

當用戶首次訪問該Default.aspx,該FirstVisitHomePage是呼吁Facade。它通過一個復雜的過程來克隆模板頁面、小部件和設置默認用戶設置等集成測試,將確保如果FirstVisitHomePage被調用參數標識一個新的用戶訪問的站點,那么它將返回可以對用戶創建的默認頁面和部件的一個對象。因此: 由于之前從來沒有誰訪問過該網站的匿名用戶, 當用戶第一次訪問, 然后在準確的列和位置作為anon_user的網頁新創建的頁面創建的小部件。

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

需要進一步的解釋:為從模板用戶發現每個頁面確保新用戶從模板用戶的頁面的部件獲得完全一樣的頁面。獲得來自新用戶的頁面的窗口小部件比較每個插件。當在做業務層的變化對于每個插件確保具有相同的名稱,狀態,位置等獨一無二的部件,我可以運行集成測試,以確保關鍵功能是否按預期工作完成,而且在整個業務層沒有破損任何地方。 我用xunit.console.exe上運行的集成測試測試并生成一個不錯html報告:

該報告使用下面的命令行產生:
d:xunitxunit.console.exe
d:trunksrcDropthings.Business.Facade.TestsbinDebugDropthings.Business.Facade.Tests.dll

/html FacadeTest.html

您可以使用GUI xUnit:

使用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.
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
翻譯來與Maxleap團隊_云服務研發成員:Sunny Zhang
中文翻譯原鏈接:https://blog.maxleap.cn/archives/855

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/8727.html

相關文章

  • 隨行付微服務測試單元測試

    摘要:輸出結果需要人工檢查的測試不是一個好的單元測試。為了有效的進行單元測試,需要遵循一定的方法,通常采用路徑覆蓋法設計單元測試用例。 在微服務架構下高覆蓋率的單元測試是保障代碼質量的第一道也是最重要的關口,應該持之以恒。 背景 單元測試為代碼質量保駕護航,是提高業務質量的最直接手段,實踐證明,非常多的缺陷完全可以通過單元測試來發現,測試金字塔提出者Martin Fowler 強調如果一個高...

    xiguadada 評論0 收藏0
  • 詳解Gradle自動實現Android組件化

    摘要:我們一般把自動化測試劃分為三種分別是單元測試目的是測試代碼的最小單元。集成測試用來測試一個完成的組件或子系統,確保多個類之間的交互是否按預期運行。集成測試需要比單元測試需要更長的執行時間,而且更加難以維護,失敗的原因難以診斷。 前言;為什么我們要用Gradle管理組件呢?先來看看Android組件化需要實現的目標按照業務邏輯劃分模塊項目模塊能夠單獨啟動測試能夠根據需求引入或刪除某些業務模塊通...

    番茄西紅柿 評論0 收藏0
  • 再談自動化測試——我們在編寫測試時,應該注意什么

    摘要:原則具體包括自動化獨立性可重復簡單的解釋一下三個原則單元測試應該是全自動執行的。為了保證單元測試穩定可靠且便于維護,需要保證其獨立性。原則編寫單元測試用例時為了保證被測模塊的交付質量需要符合原則。與設計文檔相結合來編寫單元測試。 本文首發于泊浮目的專欄:https://segmentfault.com/blog... 背景 最近項目在測試階段陸陸續續的測出了一些bug.這個情況剛出現...

    My_Oh_My 評論0 收藏0
  • 前端進階之路: 前端架構設計(3) - 測試核心

    摘要:而測試驅動開發技術并不只是單純的測試工作。需求向來就是軟件開發過程中感覺最不好明確描述易變的東西。這里說的需求不只是指用戶的需求,還包括對代碼 可能很多人和我一樣, 首次聽到前端架構這個詞, 第一反應是: 前端還有架構這一說呢? 在后端開發領域, 系統規劃和可擴展性非常關鍵, 因此架構師備受重視, 早在開發工作啟動之前, 他們就被邀請加入到項目中, 而且他們會跟客戶討論即將建成的平臺的...

    Karuru 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<