2015-12-08 40 views
1

我目前正在對一些新的功能我已經加入到我們的ASP.NET項目的一些單元測試(不,這不是測試驅動設計)。我們使用NHibernate框架並使用UnitTest模擬庫FakeItEasyFakeItEasy操作參數在單元測試,但仍執行內部操作代碼

我有以下類&方法,我想測試:

public class Round 
{ 
    public static Round Create(List<Company> activeCompanies, Period period, 
           BusinessUser user, BusinessUser systemUser, 
           ISession session, IEntityQuery entityQuery, 
           RoundProcessBuilder processBuilder) 
    { 
     var round = new Round 
     { 
      Processes = new List<Process>(); 
      Period = period, 
      CreationDate = DateTime.Now, 
      CreatedBy = user 
     }; 

     // Save the Round in the DB so we can use it's Id in the Processes: 
     session.Save(round);    

     foreach (var company in activeCompanies) 
     { 
      var companyData = session.Get<CompanyData>(company.Id); 
      var processResult = 
        roundProcessBuilder.Build(
         systemUser, 
         new CreateRoundProcessData(company, round, companyData), 
         entityQuery, 
         session); 

      processResult.HandleProcess(process => 
       { 
        // serviceBus can stay null 
        process.Create(systemUser, DateTime.Now, session, null); 
        // No need to save the session here. If something went 
        // wrong we don't want halve of the processes being saved 
        round.Processes.Add(process); 

        // It's all or nothing 
       }); 
     } 

     return round; 
    } 
} 

我主要是想測試什麼:當我使用這個Round#Create方法與假設100家活躍的公司,它應該創建100進程,並且每個進程都應該包含。

這是我的單元測試至今:

[TestFixture] 
public class RoundTest 
{ 
    private BusinessUser _systemUser; 
    private DateTime _creationDateRound1; 
    private List<Company> _activeCompanies; 
    private RoundProcessBuilder _roundProcessBuilder; 
    private ISession _session; 

    [SetUp] 
    public void Setup() 
    { 
     _creationDateRound1 = new DateTime(2015, 10, 5); 
     _systemUser = TestHelper.CreateBusinessUser(Role.Create("systemuser", "test", 
      Int32.MaxValue)); 
     _activeCompanies = new List<Company> 
     { 
      TestHelper.CreateCompany(); 
     }; 
     _roundProcessBuilder = A.Fake<RoundProcessBuilder>(); 
     _session = A.Fake<ISession>(); 
    } 

    [Test] 
    public void TestCreateRoundWithoutPreviousRound() 
    { 
     var fakeExpectedRound = Round.Create(_activeCompanies, DateTime.Now.ToPeriod(), 
      _systemUser, _systemUser, _session, null, _roundProcessBuilder); 
     var fakeExpectedRoundData = RoundProcessData.Create(TestHelper.CreateCompany(), 
      fakeExpectedRound, new CompanyData()); 
     var fakeExpectedProcess = new Process(_systemUser, null, "processName", null, 
      fakeExpectedRoundData, "controllerName", null); 
     var processSuccessResult = new ProcessSuccessResult(fakeExpectedProcess); 

     A.CallTo(() => _roundProcessBuilder.Build(null, null, null, null)) 
      .WithAnyArguments() 
      .Returns(processSuccessResult); 

     A.CallTo(() => processSuccessResult.HandleProcess(A<Action<Process>>.Ignored)) 
      .Invokes((Action<Process> action) => action(fakeExpectedProcess)); 
     var round = Round.Create(_activeCompanies, _ceationDateRound1.ToPeriod(), 
      _systemUser, _systemUser, _session, null, _roundProcessBuilder); 

     Assert.AreEqual(_activeCompanies.Count, round.Processes.Count, "Number of processes"); 
     Assert.AreEqual(round.Period.Quarter, Math.Ceiling(_creationDateRound1.Month/3.0m), "Quarter"); 
     Assert.AreEqual(round.Period.Year, round.Year, "Year"); 

     // Test if each of the processes knows the RoundId, have the proper state, 
     // and are assigned to the systemuser 
     //foreach (var process in round.Processes) 
     //{ 
     // var roundProcessData = process.ProcessData as RoundProcessData; 
     // Assert.IsNotNull(roundProcessData, "All processes should have RoundProcessData-objects as their data-object"); 
     // Assert.AreEqual(roundProcessData.Round.Id, round.Id, "RoundId"); 
     // Assert.AreEqual(process.Phase.State, PhaseState.Start, "Process state should be Start"); 
     // Assert.AreEqual(process.AssignedTo, _systemUser, "AssignedTo should be systemuser"); 
     //} 
    } 

    ... // More tests 
} 

我的問題出在下面的代碼:

A.CallTo(() => processSuccessResult.HandleProcess(A<Action<Process>>.Ignored)) 
    .Invokes((Action<Process> action) => action(fakeExpectedProcess)); 

它給出了一個 「The specified object is not recognized as a fake object.」 的錯誤。

我之所以有這部分代碼是因爲在以下部位來process沒有它爲空:

processResult.HandleProcess(process => // <- this was null 
    { 
     process.Create(systemUser, DateTime.Now, session, null); 
     round.Processes.Add(process); 
    }); 

PS:我註釋掉額外檢查的foreach在我的單元測試,因爲它最有可能是無論如何,當我嘲笑process本身時,它是毫無用處的。我的主要測試是如果創建進程並將其添加到基於所給出的活動公司的列表中。

回答

2

您的問題似乎是,你正在嘗試「假」邏輯添加到一個對象,其實也不是,假的:

// You create this as an instance of ProcessSuccessResult: 
var processSuccessResult = new ProcessSuccessResult(fakeExpectedProcess); 

...然後繼續嘗試將條件添加到在這裏:

A.CallTo(() => 
    processSuccessResult 
     .HandleProcess(A<Action<Process>>.Ignored)) 
     .Invokes((Action<Process> action) => action(fakeExpectedProcess)); 

爲了做到這最後一點,變量processSuccessResult將需要一個接口的假實例,以便FakeItEasy可以使用它,並應用所需的邏輯。

我假設ProcessSuccessResult是你有機會獲得一類,並可以編輯?如果是這樣,你應該能夠添加一個接口,它將包含你需要的方法,所以你可以在以後的工作。

一旦你定義的,你應該能夠建立你的假對象如下,其中IProcessSuccessResult將是一個假的實現你的接口,通過FakeItEasy提供:

var processSuccessResult = A.Fake<IProcessSuccessResult>(); 

現在你應該可以使用A.CallTo(...)向該虛假對象添加邏輯。

當然,這將意味着真正的實現你的ProcessSuccessResult類是包括或通過可變processSuccessResult調用。如果這部分需要,那麼你可以嘗試兩種:

  • 添加類似於它的邏輯,或者使用FakeItEasy的設置代碼的假對象調用它(儘管這可能會過於複雜),OR :
  • 添加一個單獨的變量以包含實際類的實例(即分別爲兩個變量fakeProcessSuccessResultprocessSuccessResult),並使用單獨的測試來測試此類的單獨方面以及它的用法。

我會推薦後者,如果可能的話。

我希望這已經夠清楚了,這對你會有幫助。我知道它有時可能會非常複雜,找到測試這種事情的最佳策略。

+2

謝謝!我僞造了'ProcessSuccessResult'和'HandleProcess',並且它經過了它。我現在有一個NullReferenceException,但是這是我自己的錯誤,因爲在構造函數/創建方法之一中傳遞null作爲參數。至少我現在可以繼續。看到這個解決方案現在很清楚,但是因爲我對'FakeItEasy'(或者一般的嘲諷)相當陌生,所以我錯過了它。 –