使用最小起订量测试存储库

本文关键字:测试 存储 | 更新日期: 2023-09-27 18:36:57

我正在尝试使用最小起订量测试存储库来模拟存储库的行为。我是最小起订量新手,所以请耐心等待。

给定以下方法:

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

使用最小起订量测试存储库

你的方法很好,但是我会调整一些东西。我对你的测试和模拟组件做了一些更改,你可以在下面看到。

单元测试

您在单元测试中测试的方法(见下文)与您在问题中定义的方法并不真正匹配。

单元测试方法调用:

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

测试方法:

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

假设上面的方法是另一个类的一部分,我称之为 NotesHelper - 不是存储库调用的理想名称,但它只是为了让编译工作。

我个人会将您的单元测试分成 2 个单独的单元测试。一个用于验证是否调用了所需的方法,另一个用于验证是否已从集合中删除注释。

另外,我没有创建一个模拟的提交版本,而是创建了一个假的提交版本。这是因为 SubmitVersion 中的例程可能无法模拟。您还可以执行基于状态的测试(首选)以确保版本。注释.删除(注释);已被调用。

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

附加说明:此外,提供描述性单元测试方法名称还可以提高单元测试的可读性。