【问题标题】:Testing a interface repository测试接口存储库
【发布时间】:2010-01-19 19:57:26
【问题描述】:

我想知道我应该测试什么以及应该如何测试 IRepository。

目前我在我的域中创建了一个 MemoryRepository,我正在使用假数据对其进行测试。我不确定这是否正确。首先创建一些数据然后测试存储库是否正确返回它感觉有点奇怪。

这就是我的 MemoryRepository 的样子:

public class MemoryRepositoryUser : IRepositoryUser
{
    List<User> fakeUsers;       

    public MemoryRepositoryUser(List<User> fakeUsers)
    {
        this.fakeUsers = fakeUsers;
    }

    public IEnumerable<User> GetAll()
    {
        return fakeUsers;
    }

    public User GetById(string id)
    {
        return GetAll().Where(u => u.Username == id).Single();
    }
}

这些是我写的一些测试:

[TestFixture]
class TestMemoryRepositoryUser
{
    private MemoryRepositoryUser repository;

    public TestMemoryRepositoryUser(){
        repository = new MemoryRepositoryUser(FakeData.GetFakeUsers());
    }

    [Test]
    public void Get_All_Users()
    {
        var Users = repository.GetAll();
        Assert.IsNotNull(Users);
        Assert.IsInstanceOf(typeof(IEnumerable<User>), Users);
        Assert.AreEqual(3, Users.Count());
    }

    [Test]
    public void Get_User_By_ID()
    {
        var Username = "Bob";
        var User = repository.GetById(Username);
        Assert.IsNotNull(User);
        Assert.AreEqual(Username, User.Username);
    }
}

我对测试代码还很陌生,而且我大多对我应该测试的内容有疑问。我想测试 MemoryRepository 可以帮助我在界面中创建我需要的所有功能,而无需与数据库对话?

【问题讨论】:

标签: c# asp.net-mvc testing tdd


【解决方案1】:

通常存储库测试是集成测试,它们确实与数据库对话。正如您所注意到的,使用虚假数据测试存储库毫无意义。

通常是模拟存储库以测试其他类。通过模拟除被测类之外的每个依赖项,您可以隔离正在测试的函数。为存储库声明接口的好处在于,它允许它们很容易被模拟并用于其他代码的单元测试。

【讨论】:

    【解决方案2】:

    如果您打算仅将MemoryRepositoryUser 用作存根来测试存储库客户端中的行为,那么我建议您暂时不要测试MemoryRepositoryUser 本身。 (如果你想测试一个测试替身,那就太复杂了。)相反,把你的精力集中在测试或测试驱动IRepositoryUser的生产实现上。

    另一方面,请记住IRepositoryUser 的生产实现,我将其称为AdoBasedRepositoryUser(对于您实现使用ADO 的示例),其行为方式需要与您的存根相同。如果不是,那么存储库客户端的测试可能会假定IRepositoryUser 中的错误行为。您可以考虑对此进行一些测试。

    例如,当您测试AdoBasedRepositoryUser 时,您将通过编写类似这样的测试来检查GetById()

    在 AdoBasedRepositoryUserTest... [测试] GetById_RecordFound() { 将 ID 为 762 的记录直接插入到 USERS 表中 预期用户 = ID 为 762 且设置了强制属性的用户 实际用户 = new AdoBasedRepositoryUser().GetById(762); Assert.AreEqual(预期,实际); // 实现 User.Equals() 来比较值 }

    您将需要验证 MemoryRepositoryUser 是否也通过了此测试,以便您可以使用它来存根 IRepositoryUser 对其客户端的测试。

    在 MemoryRepositoryUserTest... [测试] GetById_RecordFound() { MemoryRepositoryUser 存储库 = new MemoryRepositoryUser(); 预期用户 = ID 为 762 且设置了强制属性的用户 存储库。添加(预期); 用户实际 = repository.GetById(762); Assert.AreEquals(预期,实际); }

    只要您在MemoryRepositoryUserTest 中的测试与AdoBasedRepositoryUser 中的测试相匹配,那么您就可以确信您的存根与生产行为匹配得足以在测试使用此存储库的服务时安全地使用存根。

    完成此操作几次后,您可能已准备好查看合同测试。 (在网上搜索。)

    最后一件事:我会将您的存储库接口命名为 IUserRepository 而不是 IRepositoryUser

    【讨论】:

    • 不确定你的想法在这个答案之后的近 11 年是否相同,但无论如何我都会问:)。两个相关的问题。 1)当后端服务器关闭时,您的示例中的一项测试会是什么样子来测试行为,您将如何在测试中设置该条件? 2) 与此相关的是,在您的 other 测试中使用MemoryRepositoryUserTest 时,您将如何启用或触发“后端服务器已关闭”行为? MemoryRepositoryUserTest 上的布尔字段,您可以打开/关闭(可能是 isBackendDown)?
    • @jrahhali 我更新了答案。变化不大。
    • @jrahhali 要在“后端”关闭时测试(生产)存储库实现,我假设存储库实现 X 引用到后端 Y 的网关。我可以使用Y 上的 Crash Test Dummy 模式:存根 Y 失败,但真正的后端失败,然后检查 X 是否优雅地处理了失败。也许我们需要将 Y 隐藏在接口后面才能做到这一点。也许我们需要 Y 的合同测试来通过抛出正确的异常来表明 Y 表示失败。
    • @jrahhali In-Memory Repository 的GetById() 允许 抛出FooException,但这不是必需 这样做的。通常,UserRepository 可能会从 GetById() 抛出 FooException确实 抛出它的任何实现都需要“出于商定的原因”抛出它。这是UserRepository 接口的GetById() 合同的一部分。
    • 感谢您的周到回复。你已经回答了我的问题,并给了我一些思考的想法。
    【解决方案3】:

    我注意到可能与您的问题无关的一件事是repository 的状态应该独立于所有其他运行的测试。在这种情况下,每个测试都作用于相同的实例。

    考虑编写Delete_user 测试时会发生什么。该测试是否在Get_All_Users 之前运行并不重要。我会在每次测试中创建一个新的MemoryRepositoryUser。这种测试气味可能存在于其他测试中,并不特定于您是否应该测试假货。

    通常我会使用 sub/mock 来代替假货。正如 Jamie 所提到的,该界面允许您轻松地模拟您的依赖项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-28
      • 2013-01-20
      • 2011-03-05
      • 2017-07-16
      • 2014-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多