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

时间:2016-02-29 00:43:18

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

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

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

在我的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. 明确地将select语句发送到数据库
  3. 或者我可以通过首先创建必要的记录进行数据库单元测试(在我们的例子中,它将确保数据库中不存在111291。
  4. 除非你强烈反对选项#3,否则我倾向于实施。 如何创建部分使用数据库单元测试但通过常规单元测试调用的单元测试?

    我正在寻找以下内容:

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

2 个答案:

答案 0 :(得分:1)

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

  1. 在测试类的[Setup]方法中,设置新的事务范围并设置所需的任何先决条件。 (是否有记录)
  2. 在实际测试用例中,使用Linq2Sql或EF LINQ进行验证。它非常高效,可以避免令人讨厌的SQL /存储过程。
  3. [Teardown]方法中,完成提交事务的范围。
  4. 对任何异常,交易都会自然回滚。

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

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

    正如你正确提到的那样,你所谓的这些并不重要......它们可能是数据测试或集成测试等(不一定是单元测试)

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

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

答案 1 :(得分: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%的数据库参与下,我们让实体框架知道,“嘿EF!把我的列表视为表格。” 然后,EF会在列表中运行所有实际查询(Where,Select,OrderBy,GroupBy等)。设置此列表非常简单。 (包括嵌套链接)

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

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