数据库操作作为单元测试的先决条件?

database operations as a pre-requisite to unit tests?

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

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

在我的 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. 在 entity framework
  2. 内实施 linq 查询
  3. 显式发送 select 语句到数据库
  4. 或者我可以通过首先创建必要的记录来进行数据库单元测试(在我们的例子中,它将确保数据库中不存在 111291。

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

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

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

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

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

任何异常,事务都会自然回滚。

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

这样,您的测试就不会真正依赖于数据库单元测试,而是共享数据访问代码。

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

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

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

这里是您要求的额外答案,纯粹是 Entity Framework

    [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 知道,"hey EF! treat my List as the table." 然后 EF 运行列表中的所有实际查询(Where、Select、OrderBy、GroupBy 等)。设置此列表非常简单。 (包括嵌套链接)

您的数据层代码将始终包含在此单元测试中。 当涉及实际 table 时,您可以相信 EF 会做出正确的 SQL 调用。

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