【问题标题】:database operations as a pre-requisite to unit tests?数据库操作作为单元测试的先决条件?
【发布时间】:2016-02-29 00:43:18
【问题描述】:

如何创建部分使用数据库单元测试但从常规单元测试调用的单元测试?

是的,也许它们可能不是单元测试;您可能希望将它们称为集成测试。不管你想给它们贴什么标签,它们都是测试。

在我的 nunit 测试中,我使用了辅助常量:

private const string NumericSerial="123123";
private const string NonNumericSerial="123123 bad serialnumber";
private const int ValidPatientId=123;
private const int InvalidPatientId=-1;
private const int PatientIdThatDoesNotExistInppp=111291;
private const string SerialNumberThatDoesNotExistInshmk="123123";
private const string SerialNumberThatDoesExistInshmk="1015873";
private const byte InvalidFirmwareVersion=0;
private const int FacilityThatDoesntExistInAAA=Int32.MaxValue;

在运行任何使用这些常量的测试之前,我想验证这些常量是否正确定义。

例如,我可以断言 NumericSerial 确实是数字,而无需对任何依赖项进行任何模拟或注入 - 我只需 tryparse 它。

但是,其他常量,例如 PatientIdThatDoesNotExistInppp 需要验证它在 ppp 数据库中确实不存在。为了做到这一点,我可以遵循几条路线:

  1. 在实体框架内实现 linq 查询
  2. 向数据库显式发送一条选择语句
  3. 或者我可以通过首先创建必要的记录来进行数据库单元测试(在我们的例子中,它将确保数据库中不存在 111291。

除非您强烈反对选项 #3,否则我倾向于实施该选项。 如何创建部分使用数据库单元测试但从常规单元测试调用的单元测试?

我正在寻找类似以下的内容:

[Test]
public response_from_database_unit_test_equals_111291()
{
//call the database unit test
//retrieve value from database unit test
}

【问题讨论】:

  • 您是否首先使用实体​​框架代码作为您的数据访问层,而根本没有存储过程?如果是,那么有一种绝妙的方法来进行数据测试,无需访问数据库,但数据层具有 100% 的可靠性。让我知道。
  • 我不喜欢听听如何以这种方式进行
  • 通过“这种方式”我假设你的意思是你的问题,所以我已经回答了
  • 我的意思是通过 EF,但请随时做出单独的回答
  • 我不知道我真的在为此苦苦挣扎:drive.google.com/file/d/0ByqKtGmtuLzZUGVNVkdVaDYzOGc/…

标签: c# visual-studio unit-testing visual-studio-2015 nunit


【解决方案1】:

这是您基于纯实体框架请求的附加答案

    [TestMethod]
    public void GetAllBlogs_Orders_By_Name()
    {
        var data = new List<Blog>
        {
            new Blog { Name = "BBB" },
            new Blog { Name = "ZZZ" },
            new Blog { Name = "AAA" },
        }.AsQueryable();

        var mockSet = new Mock<DbSet<Blog>>();

        mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(0 => data.GetEnumerator());

        // we tell EF that treat our data list as the table of Blogs.
        var mockContext = new Mock<BloggingContext>();
        mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);

        // make EF query the blogs table (i.e. our list)
        var context = mockContext.Object;

        // EF has queried our list here
        // you could run Select, OrderBy, Where etc. as well
        var blogs = context.Blogs.ToList(); 

        Assert.AreEqual(3, blogs.Count);
        Assert.AreEqual("AAA", blogs[0].Name);
        Assert.AreEqual("BBB", blogs[1].Name);
        Assert.AreEqual("ZZZ", blogs[2].Name);
    }

这个示例不是我写的。它直接来自https://msdn.microsoft.com/en-us/library/dn314429.aspx(所有功劳归于 MSDN)

关键是,在数据库参与率为 0% 的情况下,我们已经让 Entity Framework 知道,“嘿,EF!把我的 List 当作表。” 然后,EF 运行您列表中的所有实际查询(Where、Select、OrderBy、GroupBy 等)。设置这个列表非常简单。 (包括嵌套链接)

您的数据层代码将始终包含在此单元测试中。 当涉及到实际表时,您可以信任 EF 进行正确的 SQL 调用。

附言我要注意的唯一两件事是 Include 子句和 DbFunctions。这两个在本地列表和实际表之间表现不同。但是一旦你注意了这两件事,对你的数据层进行这种程度的单元测试是一件令人愉快的事情,根本不用担心真正的数据库。

【讨论】:

  • 这太棒了,但是什么时候可以对数据运行选择查询?
  • 我不知道我真的在为此苦苦挣扎:drive.google.com/file/d/0ByqKtGmtuLzZUGVNVkdVaDYzOGc/…
  • 您设置的设备不正确.. 使用这个 var mockContext = new Mock(); mockContext.Setup(c => c.Devices).Returns(mockSet.Object);
  • 什么是 BlogService ?
  • 不重要。它只是从上下文中查询博客的包装器。我已经编辑了我的答案以直接使用上下文。基本上,我们通过 List.. 设置 Blogs 表数据,然后当 EF 在 context.Blogs 上运行查询时,它实际上是在我们的 List 上运行它
【解决方案2】:

如果您想进行与数据库相关的测试,那么有几个选项可以让您更轻松。 (它不会 100% 无皱纹,因为 DB 测试并不简单)

  1. 在您的测试类的[Setup] 方法中,设置一个新的事务范围并设置您需要的任何先决条件。 (有无记录)
  2. 在实际测试用例中,使用Linq2Sql 或EF LINQ 进行验证。它非常高效,并且避免了讨厌的 SQL/存储过程。
  3. [Teardown]方法中,完成提交事务的作用域。

在任何异常情况下,事务都会自然回滚。

而不是一个单元测试调用数据库单元测试,而是将通用代码提取到通用方法中,并将它们称为您的数据库测试和这些测试。 (例如 GetRecordWithId(5678))

这样,您的测试实际上并不依赖于数据库单元测试,而是共享数据访问代码。

正如您正确提到的,您如何称呼这些并不重要。它们可能是数据测试或集成测试等(不一定是单元测试)

我们可以尽最大努力使用事务范围保持数据库状态清洁,但最终会出现连接问题、并行测试执行、在连接到公共数据服务器的开发服务器上执行测试(或本地 sql 服务器先决条件),在构建服务器等上执行测试。当涉及到数据库测试时,当涉及到实际数据库时会产生问题。

许多团队采用一种策略,即建立一个特定于测试运行的新数据库 [prefix+timestamp],这样它就不会与其他运行发生冲突。并在它的最后拆除分贝。 (最坏的情况,有一个后台进程,它监视具有特定前缀名称的数据库,并根据时间戳在每个午夜清理它。正如您所看到的,有很多外围工作要做,对于测试的价值数据层。

【讨论】:

  • 一如既往地感谢您的友好、详尽的反馈。您能否指出一个从头到尾显示此类测试的示例,或者可能只是 [setup]?
  • raja 如果您还可以包含一个使用事务范围的示例,我将不胜感激
猜你喜欢
  • 2011-07-10
  • 2011-11-29
  • 1970-01-01
  • 2018-04-24
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 2021-03-03
  • 2018-04-19
相关资源
最近更新 更多