【问题标题】:Dapper: Unit Testing SQL QueriesDapper:单元测试 SQL 查询
【发布时间】:2013-12-09 01:24:19
【问题描述】:

我从 Dapper(微型 ORM)开始,我使用 Dapper Rainbow。我想测试查询和它们检索到的数据。

我的意思是,例如,我有 UserService 和方法 GetAll(),我想测试 sql 查询是否从某个列表(不是来自数据库,因为我希望测试速度快)。你知道我该怎么做吗?

我的服务类(以及我要测试的方法):

public static class UserService{
    public static IEnumerable<User> GetAll(){
        return DB.Users.All();
    }
}

您对单元测试查询和数据检索有什么建议吗?

谢谢

【问题讨论】:

  • 您需要模拟您的数据存储。 DB 是什么?
  • 你模拟了数据源。用测试数据注入它,看看它是否返回正确的数据。那里有许多模拟框架,Moq 和 RhinoMock 浮现在脑海中。选择一个开始吧!

标签: c# .net unit-testing tdd dapper


【解决方案1】:

我建议阅读依赖注入和存储库模式。如果你采用上面代码中的方法,你将很难模拟出依赖关系,因为类和方法是静态的。

这是一个更好的方法。

public interface IUserRepository
{
   IEnumerable<User> GetAll()
}

public class UserRepository : IUserRepository
{
  public IEnumerable<User> GetAll()
  { 
    return DB.Users.All();
  }
}

public class UserService
{
    IUserRepository _userRepository;
    public UserService(IUserRepository userRepository)
    {
      _userRepository = userRepository
    }

    public Enumerable<User> GetAll(){
        return _userRepository.GetAll();
    }
}

现在为了测试,您可以模拟您的存储库。我使用了一个名为 NSubstitute 的模拟框架,在我看来它比上面提到的其他框架要简单得多,但这是个人喜好。首先介绍如何在没有任何模拟框架的情况下编写测试。

public class FakeUserRepository : IUserRepository
{
  public IEnumerable<User> GetAll()
  { 
    return new List<User> { new User {FirstName='Bob', LastName='Smith'}, };
  }
}

在你的测试中

[Test]
public void GetAll_ShouldReturnAllFromFake()
{
   // Arrrange
   var userService = new UserService(new FakeUserRepository())
   // Act
   var result = userService.GetAll();
   // Assert
   var user = result[0];
   Assert.AreEqual("Bob", user.FirstName);
   Assert.AreEqual("Smith", user.LastName);   
}

这个例子有点做作,因为测试是否可以从假存储库中取回数据并没有真正意义。在现实中如何使用它是,如果您的服务中有一些业务逻辑,比如找回用户,然后检查他们是否超过了某个年龄或其他东西。例如UserService 上的 IsLegalDrivingAge 方法。

【讨论】:

  • 谢谢,很有帮助。但是,我最需要的是测试 sql 查询,并且我的类的一些方法运行原始查询。有什么方法可以加载可通过查询检索的假数据?你知道我该如何测试它吗?谢谢!
  • 您可以在 test setup 方法中启动一个事务,然后在 teardown 方法中回滚该事务。这意味着数据库保持一致状态。您应该有一个没有数据的数据库副本用于测试目的,创建一个测试包来加载数据。请注意,这更像是一个集成测试而不是单元测试。你也看过精巧的单元测试,它们可能会准确地告诉你你在寻找什么。
  • Andrew,很好地说明了集成测试和单元测试之间的区别。得到我的 +1。
  • @Andrew 我一直试图向我的同事解释这个This example is a little contrived as it doesn't really make sense to test that you can get data back from a fake repository。我们有一个假数据库,它不是很单位
  • @Andrew,为存储库模式竖起大拇指,但我认为这不是 OP 所要求的。好吧,至少我是如何得到它的,问题更多是关于如何测试小巧的 SQL 查询本身,而不是对数据库进行实际查询,比如这个答案:stackoverflow.com/a/35557936/2571926。由于查询很可能包含一些逻辑(过滤/排序/聚合),因此以某种方式对其进行测试也是有意义的。
【解决方案2】:

使用 Dapper,您的 SQL 可能是字符串文字,可能与 C# 条件混合,语法未经验证,数据库引用可能错误。你测试的直觉是好的。但是,针对真实数据库运行代码是判断它是否输出有效查询的唯一方法。所以这里需要的测试是集成测试。这并不难,您可以使用您的单元测试框架来执行此操作,但由于测试必须针对真正的数据库,您可能不想在运行单元测试的任何地方都运行它,而不是在构建服务器上。

然后,由于 Dapper 是 ADO 的扩展方法,要对使用查询的代码进行单元测试,您需要将其包装在存储库模式中。 Dapper Wrapper 似乎是这里的工具。

如果所有这些看起来都不必要地困难,请尝试QueryFirst(免责声明:我写的)。你在一个真正的 sql 窗口中编写你的 SQL,连接到你的数据库,在你输入时验证 sql。每次保存文件时,您的查询都会针对您的数据库进行集成测试,无需您费力。然后,如果查询运行,QueryFirst 会生成包装代码以供您使用,包括一个接口,以便您在对消费代码进行单元测试时轻松模拟真实查询。这必须是向前迈出的一步,不是吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-16
    • 2014-07-07
    • 2015-03-07
    • 2011-05-15
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 2012-02-25
    相关资源
    最近更新 更多