2013-10-24 44 views
3

我想測試一個使用起訂量來嘲笑回購行爲的存儲庫。我是最小起訂量的新故障,請耐心等待。使用起訂量來測試存儲庫

考慮以下方法:

public static SubmissionVersion DeleteNote(IRepository repository, SubmissionVersion version, Guid noteId) 
{ 
    Note note = repository.GetById<Note>(noteId); 
    version.Notes.Remove(note); 
    repository.Save(version); 
    repository.Delete(note); 
    return repository.GetById<SubmissionVersion>(version.Id); 
} 

這是否測試一下好不好?

[Fact] 
public void DeleteNoteV2() 
{ 
    // Arrange 
    var note = new Note{ Id = Guid.NewGuid()}; 

    var subVersion = new Mock<SubmissionVersion>(); 
    subVersion.Setup(x => x.Notes.Remove(note)); 

    var repo = new Mock<IRepository>(); 
    repo.Setup(x => x.GetById<Note>(note.Id)).Returns(note); 
    repo.Setup(x => x.GetById<SubmissionVersion>(It.IsAny<Guid?>())).Returns(subVersion.Object); 

    // Act 
    SubmissionVersion.DeleteNote(repo.Object, subVersion.Object, note.Id.Value); 

    // Assert 
    repo.Verify(x => x.GetById<Note>(note.Id), Times.Once()); 
    repo.Verify(x => x.Save(subVersion.Object), Times.Once()); 
    repo.Verify(x => x.Delete(note), Times.Once()); 

    subVersion.Verify(x => x.Notes.Remove(It.IsAny<Note>()), Times.Once()); 
} 
+0

對我來說很不錯 – ayls

+1

測試它的一個好方法是將您的代碼註釋掉,並確認測試在預期的點失敗,然後再次包含代碼並確保它通過...這就是TDD這樣一種很好的方法,因爲它可以證明你正在編寫的測試。 –

回答

8

你的方法很好,但我會調整一些東西。我對你的測試和模擬組件做了一些改變,你可以在下面看到。

單元測試

您在單元測試中的測試方法(見下文),並沒有真正匹配,你在你的問題中定義的方法。

單元測試方法調用:

public static SubmissionVersion DeleteNote 
    (IRepository repository, SubmissionVersion version, Guid noteId) 

我認爲上面的方法是另一個階級的一部分,我把它叫做爲NotesHelper - 不是理想的名稱:

SubmissionVersion.DeleteNote(repo.Object, subVersion.Object, note.Id.Value); 

下的測試方法存儲庫調用,但它只是爲了讓編譯工作。

我會親自把你的單元測試分成2個單獨的單元測試。一個用於驗證是否調用了所需的方法,另一個用於驗證筆記是否已從集合中刪除。

而不是創建一個模擬SubmissionVersion,我創建了一個fakeSubmissionVersion。 這是因爲SubmissionVersion中的例程可能不可嘲弄。您還可以執行基於狀態的測試(首選)以確保版本。註釋。刪除(note);已被調用。

[Fact] 
public void DeleteNote_Deletion_VerifyExpectedMethodsInvokecOnlyOnce() 
{ 
    // Arrange 
    var note = new Note { Id = Guid.NewGuid() }; 
    var fakeSubmissionVersion = new SubmissionVersion() { Notes = new List<Note>() }; 
    var repo = new Mock<IRepository>(); 
    repo.Setup(x => x.GetById<Note>(It.IsAny<Guid>())).Returns(note); 

    // Act 
    NotesHelper.DeleteNote(repo.Object, fakeSubmissionVersion, note.Id.Value); 

    // Assert 
    repo.Verify(x => x.GetById<Note>(note.Id), Times.Once()); 
    repo.Verify(x => x.Save(fakeSubmissionVersion), Times.Once()); 
    repo.Verify(x => x.Delete(note), Times.Once()); 
} 

上述測試可以通過在自己的測試中定義每個驗證來進一步改進。

[Fact] 
public void DeleteNote_Deletion_VerifyDeleteMethodCalledOnlyOnce() 

這種方式,如果一個測試失敗,我們會確切地知道哪個方法沒有被期望調用。有時候,如上所述進行多項驗證可能會造成問題。例如,如果尚未調用Save方法,則永遠不會知道Delete方法是否已被調用。這是因爲在尚未調用Save方法並且測試執行已終止時引發了異常。

這將導致多個測試,但它們更具可讀性和可維護性。當然,您可以將通用代碼重構爲工廠或輔助方法。

[Fact] 
public void DeleteNote_RemoveNotes_ReturnsExpectedNotes() 
{ 
    // Arrange 
    var note = new Note { Id = Guid.NewGuid() }; 
    var fakeSubmissionVersion = new SubmissionVersion() { Notes = new List<Note>() { note } }; 
    var repo = new Mock<IRepository>(); 
    repo.Setup(x => x.GetById<Note>(It.IsAny<Guid>())).Returns(note); 

    // Act 
    NotesHelper.DeleteNote(repo.Object, fakeSubmissionVersion, note.Id.Value); 

    // Assert 
    Assert.AreEqual(0, fakeSubmissionVersion.Notes.Count); 
} 

附加註釋:另外提供一個描述單元測試方法的名稱提高了單元測試的可讀性。

+0

非常好的答案!感謝您的洞察! – Sam

+0

您能否爲我解釋一下這個問題:'您在單元測試中測試的方法(見下文),與您在問題中定義的方法不匹配。「# – Sam

+1

@Sam,no prob。這是關於你的問題有「給定以下方法」:public static SubmissionVersion DeleteNote(IRepository repository,SubmissionVersion version,Guid noteId) - 我相信這是你測試的方法(被測方法)。但是不清楚你的測試目標是什麼類(被測系統(SUT))。你也有...新的模擬()所以我不能推斷出你的SUT,即使你的測試意圖是清楚的。所以我介紹了NotesHelper(不理想)作爲你的SUT。 – Spock