【问题标题】:Is it worth testing data access queries themselves?是否值得自己测试数据访问查询?
【发布时间】:2014-03-27 15:44:03
【问题描述】:

我知道正确的 SOLID 原则与 IOC 相结合意味着您可以通过模拟类依赖项来对所有代码进行单元测试,而无需实际访问数据库。在工作中讨论之后,我的问题是是否值得实际测试您的数据访问层本身。

当您正确分离代码后,您的数据访问方法通常会非常小。这是一个人为的例子:

public class InvoiceQueries
{
    public IEnumerable<Customer> GetAllCustomersWithOverdueInvoices(TimeSpan timeOverdue)
    {
        var results = 
            from invoice in Invoices
            where invoice.DateDue - timeOverdue > DateTime.Now
            select invoice.Customer;

        return results;
    }
}

是否值得编写自动化测试(它不会是单元测试)来检查您的查询是否正确编写?也许连接到一个真实的数据库,甚至是一个内存数据库,插入一些测试数据,然后检查你的方法是否返回了这些客户?

一位同事说你绝对应该 - 一切都应该经过测试。对我来说,要在构建服务器上启动并运行某种形式的 T-SQL 数据库似乎需要做大量工作,更不用说这些测试几乎肯定会非常慢,而且有用性令人怀疑。

谁有实际测试(以孤立的方式)他们的数据访问层的经验?

【问题讨论】:

    标签: sql database unit-testing testing data-access-layer


    【解决方案1】:

    是的,它应该经过测试,值得花时间让它工作(否则你会浪费时间手动测试它)。是的,它比单元测试慢得多,但你可以让它足够快(内存数据库,所有测试的一次数据库启动,测试组等)。当然,您需要将 db 层与您的业务逻辑隔离开来

    【讨论】:

      【解决方案2】:

      经过深思熟虑,我和我的同事想出了一种将查询与持久层分开的方法。我在下面整理了一个简单的例子:

      public class Invoice
      {
          public int Id {get; set;}
          public DateTime DueOn {get; set;}
          public DateTime MadeOn {get; set;}
          public decimal Total {get; set;}
          public Customer Customer {get;set;}
      }
      
      public class Customer
      {
          public int Id {get; set;}
          public string Name {get; set;}
      }
      
      public class OverdueInvoice
      {
          public int CustomerId {get; set;}
          public int InvoiceId {get; set;}
          public string CustomerName {get; set;}
          public decimal Total {get; set;}
      }
      
      public class InvoiceQueries
      {
          private readonly IReadRepository<Invoice> _invoices;
      
          public InvoiceQueries(IReadRepository<Invoice> invoices)
          {
              _invoices = invoices;
          }
      
          public IEnumerable<OverdueInvoice> GetAllOverdueInvoices(TimeSpan timeOverdue)
          {
              return _invoices
                  .FindAll(invoice => invoice.DueOn - timeOverdue > DateTime.Now)
                  .Select(Map);
          }
      
          private OverdueInvoice Map(Invoice invoice)
          {
              return new OverdueInvoice
              {
                  CustomerId = invoice.Customer.Id,
                  InvoiceId = invoice.Id,
                  CustomerName = invoice.Customer.Name,
                  Total = invoice.Total,
              };
          }
      }
      
      public class InMemoryInvoicesRepository : IReadRepository<Invoice>, IWriteRepository<Invoice>
      {
          private List<Invoice> _backingStore;
      
          public InMemoryInvoicesRepository() : this(new List<Invoice>())
          {}
      
          public InMemoryInvoicesRepository(List<Invoice> backingStore)
          {
              _backingStore = backingStore;
          }
      
          public IEnumerable<Invoice> FindAll(Predicate<Invoice> predicate)
          {
              return _backingStore.FindAll(predicate);
          }
      
          public void Add(Invoice item)
          {
              _backingStore.Add(item);
          }
      }
      
      public interface IReadRepository<T>
      {
          IEnumerable<T> FindAll(Predicate<T> predicate);
      }
      
      public interface IWriteRepository<T>
      {
          void Add(T item);
      }
      

      将数据访问分离为“查询”对象和“存储库”对象允许您在测试时将存储库替换为内存中的集合。这样,您可以使用所需的任何对象填充列表,然后通过针对该列表运行查询层来测试您的查询层。存储库是一个非常愚蠢的对象,它接受您的查询并将其传递给您的 ORM(要实际执行此功能,可能必须用 Expression> 替换)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-20
        • 1970-01-01
        • 2014-08-01
        • 1970-01-01
        • 2010-09-15
        • 2010-10-12
        • 1970-01-01
        • 2015-09-22
        相关资源
        最近更新 更多