【问题标题】:What is the purpose of unit testing an interface repository对接口存储库进行单元测试的目的是什么
【发布时间】:2011-03-05 05:18:44
【问题描述】:

我正在对用于检索Customer 类型对象的ICustomerRepository 接口进行单元测试。

  • 作为一个单元测试,以这种方式测试ICustomerRepository 可以获得什么价值?
  • 以下测试在什么情况下会失败?
  • 对于这种性质的测试,是否建议进行我知道会失败的测试?即当我知道我只在存储库中放置了5 时,查找 id 4

我可能遗漏了一些明显的东西,但实现ICustomerRepository 的类的集成测试似乎更有价值。

[TestClass]
public class CustomerTests : TestClassBase
{
    private Customer SetUpCustomerForRepository()
    {
        return new Customer()
        {
            CustId = 5,
            DifId = "55",
            CustLookupName = "The Dude",
            LoginList = new[]
            {
                new Login { LoginCustId = 5, LoginName = "tdude" },
                new Login { LoginCustId = 5, LoginName = "tdude2" }
            }
        };
    }

    [TestMethod]
    public void CanGetCustomerById()
    {
        // arrange
        var customer = SetUpCustomerForRepository();
        var repository = Stub<ICustomerRepository>();

        // act
        repository.Stub(rep => rep.GetById(5)).Return(customer);

        // assert
        Assert.AreEqual(customer, repository.GetById(5));
    }
}

测试基类

public class TestClassBase
{
    protected T Stub<T>() where T : class
    {
        return MockRepository.GenerateStub<T>();
    }
}

ICustomerRepository 和 IRepository

public interface ICustomerRepository : IRepository<Customer>
{
    IList<Customer> FindCustomers(string q);
    Customer GetCustomerByDifID(string difId);
    Customer GetCustomerByLogin(string loginName);
}

public interface IRepository<T>
{
    void Save(T entity);
    void Save(List<T> entity);
    bool Save(T entity, out string message);
    void Delete(T entity);
    T GetById(int id);
    ICollection<T> FindAll();
}

【问题讨论】:

    标签: c# unit-testing tdd rhino-mocks


    【解决方案1】:

    我可能遗漏了一些东西,但您的测试的每个方面似乎都被模拟了?

    一般来说,您只模拟不是测试核心的对象。在这种情况下,您可以将此存储库用作您希望对存储库执行某些操作以检索客户 #5 并对其执行操作的函数的源。

    例如,您可以模拟客户存储库,以便您可以调用验证用户登录的方法。您将使用您的模拟存储库来防止您的单元测试依赖于真实的数据源,而不是测试您的模拟存储库。

    【讨论】:

      【解决方案2】:

      测试规则#1:

      在编写测试之前,请了解测试的目的以及它试图证明的内容。如果你不知道它证明了什么,那就没用了:)

      正如其他发帖人所说的那样,您正在对接口进行存根,然后调用存根——这并不能证明您的生产代码是否有效。

      什么是存根?

      存根用于提供固定值来驱动被测类的某些方面。例如。假设您有一个CustomerService,它有一个ICustomerRepository 类型的实例。如果您想看到CustomerService 可以在存储库为空时优雅地处理错误情况,您将存根ICustomerRepositoryGetCustomerById 方法以返回nothing/null/抛出异常,然后确保@ 987654327@ 方法做了正确的事情(例如返回未找到客户的结果)。

      即存根只是帮助您达到感兴趣的特定条件/行为的协作者。我们正在测试CustomerService,而存根的ICustomerRepository 只是帮助我们实现目标。

      您不是第一个提出这个问题的人 :)。我通常建议开发人员hand-roll their test doubles at first。它有助于了解所有交互以及框架实际上为您做什么。

      【讨论】:

        【解决方案3】:

        根据定义,接口只是合约,因此无需测试代码。您想针对接口的具体实现编写单元测试,因为那是实际执行代码所在的地方。

        如果接口没有具体的实现,而您只是在模拟具体的实现,那么您的单元测试只是基于模拟。这并没有多大价值。

        【讨论】:

          【解决方案4】:

          你的测试对我来说毫无意义。您正在测试一个预编程的存根是否正在返回您提供给它的值,您没有在测试任何真正的代码。

          关注 dcp 的回复。接口是方法的声明。你应该测试这些的实现。

          【讨论】:

          • 怎么样?在我阅读的任何地方,我们都被教导为我们的单元测试模拟一个界面。我们如何对具体实现进行单元测试?我认为按照我的理解应该避免这种情况。你能举个例子吗?谢谢!
          • 接口是一种规范,一种声明,而不是您可以运行(并因此进行测试)的代码。您在具体的类中实现该规范,这就是您将运行和测试的代码。您可以使用模拟来实现接口,以帮助您测试依赖于实现该接口的对象的其他代码。您不是“测试接口”,而是通过在这些实现上调用接口方法来测试该接口的实现是否正确(“针对接口进行测试”)。
          • 愚蠢的例子:eat 的事物的 Eater 接口,由 Dog 实现并由 Masterfeed(Eater pet) 方法中调用。当您测试Dog 时,您将通过在狗实例上调用eat 来测试Eater 接口的实现,并查看例如狗体重增加。当您测试Masters feed() 方法时,您创建一个模拟Eater,将其传递给该方法,并检查是否调用了eat
          • 在做了一些单元测试后我已经理解了这个想法。谢谢。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-08-26
          • 1970-01-01
          • 1970-01-01
          • 2023-02-24
          • 2011-03-08
          • 1970-01-01
          • 2021-10-06
          相关资源
          最近更新 更多