2013-10-28 37 views
2

給出下面,爲什麼這個單元測試失敗,他說:單元測試失敗 - 起訂量不調用方法

預計在模擬調用一次,但爲0次:X => x.Steps.Remove( It.IsAny())

配置設置:

x => x.Steps.Remove(It.IsAny<Step>()), Times.Never 
No invocations performed. 

代碼

public static SubmissionVersion DeleteStep(IRepository repository, SubmissionVersion version, Guid stepId) 
    { 
     Step step = repository.GetById<Step>(stepId); 
     Level level = step.Level; 

     /// Delete child objects 
     Step.DeleteNotifications(repository, step); 

     /// Delete Step 
     version.Steps.Remove(step); 
     repository.Save(version); 
     repository.Delete(step); 

     /// Reorder Levels 
     IList<Level> levels = new List<Level>(); 
     version.Steps.Where(s => s != step).OrderBy(s => s.Level.SortOrder).ForEach(s => 
     { 
      if (!levels.Contains(s.Level)) 
       levels.Add(s.Level); 
     }); 

     Level prevLevel = null; 
     levels.ForEach(lvl => 
     { 
      if (prevLevel != null) 
      { 
       lvl.DependsOnLevel = prevLevel; 
       repository.Save(lvl); 
      } 
      prevLevel = lvl; 
     }); 

     /// Delete Level 
     if (!levels.Contains(level)) 
      repository.Delete(level); 


     int order = 0; 
     List<Step> sorted = version.Steps.OrderBy(c => c.SortOrder).ToList(); 
     sorted.ForEach(c => 
     { /// Update SortOrder for list items 
      c.SortOrder = order++; 
      repository.Save(c); 
     }); 
     return repository.GetById<SubmissionVersion>(version.Id); 
    } 

測試

[Fact] 
public void Should_Call_SubmissionVersionRemoveStepsOnce() 
{ 
    // Arrange 
    var ctx = new TestContext(); 
    ctx 
     .SubmissionVersion 
     .SetupGet(x => x.Steps) 
     .Returns(new List<Step> { ctx.Step }); 

    // Act 
    SubmissionVersion.DeleteStep(
     ctx.Repository.Object, 
     ctx.SubmissionVersion.Object, 
     ctx.Step.Id.Value); 

    // Assert 
    ctx 
     .SubmissionVersion 
     .Verify(x => x.Steps.Remove(It.IsAny<Step>()), Times.Once()); 
} 

的TestContext

internal class TestContext 
{ 
    // Objects 
    public Note Note { get; private set; } 
    public Section Section { get; private set; } 
    public Step Step { get; private set; } 
    public SubmissionVersionProjectType SubmissionVersionProjectType 
     { get; private set; } 
    public SubmissionVersionStatusHistory SubmissionVersionStatusHistory 
     { get; private set; } 

    // Mocks 
    public Mock<SubmissionVersion> SubmissionVersion { get; private set; } 
    public Mock<IRepository> Repository { get; private set; } 

    public TestContext() 
    { 
     Note = new Note { Id = Guid.NewGuid() }; 
     Section = new Section { Id = Guid.NewGuid(), SortOrder = 0 }; 
     Step = new Step() { Id = Guid.NewGuid() }; 
     SubmissionVersionProjectType 
      = new SubmissionVersionProjectType { Id = Guid.NewGuid() }; 
     SubmissionVersionStatusHistory 
      = new SubmissionVersionStatusHistory { Id = Guid.NewGuid() }; 

     SubmissionVersion = new Mock<SubmissionVersion>(); 
     SubmissionVersion.Setup(x => x.Id).Returns(Guid.NewGuid()); 
     SubmissionVersion.Setup(x => x.Notes.Remove(Note)); 
     SubmissionVersion.Setup(x => x.Sections.Remove(Section)); 
     SubmissionVersion.Setup(x => x.Steps.Remove(It.IsAny<Step>())); 
     SubmissionVersion.Setup(x => x.SubmissionVersionProjectTypes.Remove(SubmissionVersionProjectType)); 
     SubmissionVersion.Setup(x => x.SubmissionVersionStatusHistory.Remove(SubmissionVersionStatusHistory)); 

     Repository = new Mock<IRepository>(); 
     Repository.Setup(x => x.GetById<Note>(Note.Id)).Returns(Note); 
     Repository.Setup(x => x.GetById<Section>(Section.Id)).Returns(Section); 
     Repository.Setup(x => x.GetById<Step>(Step.Id)).Returns(Step); 
     Repository.Setup(x => x.GetById<SubmissionVersionProjectType>(SubmissionVersionProjectType.Id)).Returns(SubmissionVersionProjectType); 
     Repository.Setup(x => x.GetById<SubmissionVersionStatusHistory>(SubmissionVersionStatusHistory.Id)).Returns(SubmissionVersionStatusHistory); 
     Repository.Setup(x => x.GetById<SubmissionVersion>(SubmissionVersion.Object.Id)).Returns(SubmissionVersion.Object); 
    } 
} 
+0

在實際測試中,您在模擬上設置了步驟獲取器,以返回步驟{新列表 {ctx.Step}}的具體實現。所以不是在模擬上調用Remove,而是調用List上的真正實現。 –

+0

因此,當實際的代碼調用刪除,這不會從傳入的mock.object中刪除項目? – Sam

+1

它從列表中刪除通過Steps屬性返回的項目。模擬沒有跟蹤。事實上,我從來沒有見過這樣的設置,我認爲爲「孩子」方法或財產設置模擬器並不正確​​。您正在嘲笑SubmissionVersion類型,您應該只設置此類型的屬性和方法。您可以通過直接檢查Count屬性來驗證項目是否已從列表中刪除。如果項目被刪除,它將爲0. –

回答

1

我認爲你缺少一個安裝程序。 Moq會知道.Steps(你已經設置了這個),但它不知道.Remove。

ctx.SubmissionVersion.Setup(x => x.Steps.Remove(It.IsAny<Step>())); 

所以,你的測試將是..

[Fact] 
    public void Should_Call_SubmissionVersionRemoveStepsOnce() 
    { 
     // Arrange 
     var ctx = new TestContext(); 
     ctx 
      .SubmissionVersion 
      .SetupGet(x => x.Steps) 
      .Returns(new List<Step> { ctx.Step }); 
     var submissionVersion = ctx.SubmissionVersion.Object; 

     ctx.SubmissionVersion.Setup(x => x.Steps.Remove(It.IsAny<Step>())); 

     // Act 
     submissionVersion.DeleteStep(
      ctx.Repository.Object, 
      ctx.SubmissionVersion.Object, 
      ctx.Step.Id.Value); 

     // Assert 
     ctx 
      .SubmissionVersion 
      .Verify(x => x.Steps.Remove(It.IsAny<Step>()), Times.Once()); 
    } 

現在你刪除Steps.Remove或致電不是從你的SUT(submissionVersion)一次,測試將失敗。只有在測試通過後纔會打電話。

+0

因此,當您使用MOQ來「模擬」一個對象時,「設置」定義了您期望的行爲?這是一個公平的聲明?所以即使'Steps'是一個集合,而'Remove'是一個集合方法,我仍然必須「模擬」Remove方法? – Sam

+1

正確,通常你不會在一個方法上設置,然後驗證它。例如,想象一下你模擬並注入到SUT中的虛擬類型,然後你可以驗證被調用的方法。但在你的情況下,你不能控制'刪除',因爲它是你提供的新列表,而不是一個模擬類型。所以我認爲解決方案就是設置,這意味着你要調用方法,但是在你調用SUT之後,你可以驗證方法是否被調用。可能有更好的方法,但我認爲我提供的代碼是足夠的。 – Spock