【问题标题】:Unit testing - database data单元测试——数据库数据
【发布时间】:2014-02-20 22:39:38
【问题描述】:

我尝试为一些具有存储过程的方法创建单元测试。以下是我的问题。

  • 如何对使用大量存储过程的项目进行单元测试 从数据库中获取数据进行验证?

    • 如果我必须从我的单元测试中调用这些方法,我实际上是 使 n 没有数据库调用...值得吗?

    • 如果我必须使用模拟数据,如何从 本地数据库?

    • 如何模拟存储过程调用来使用模拟数据?

【问题讨论】:

  • 您想测试调用存储过程的哪一部分?您是否正在编写自己的 SQL 类? (对于常规应用程序,您只需模拟数据持久层,而不必担心单元测试中的数据库调用......)
  • 要验证一个字段,它会进行几次数据库调用,并根据它返回的数据对这些字段进行几次验证并返回真或假。给定场景,你将如何模拟?模拟数据持久层是什么意思?请详细说明。非常感谢您的回复!
  • Jeroen Vannevel 有非常详细的答案 (+1)。对于这种情况,您确实不需要 单元测试。

标签: c# sql-server unit-testing stored-procedures


【解决方案1】:

您正在查看集成测试,而不是单元测试。集成测试依赖于外部系统(日期时间、数据库、文件系统、网络服务等),而单元测试的范围仅限于一个单元(一个或多个类中的一个或多个方法)。

首先要意识到,为了进行集成测试,您将依赖外部系统。您永远不想针对您的生产环境进行测试,并且希望保持对测试数据的控制。

因此,您应该创建一个与现有数据库完全相同的数据库,但会去除所有数据并改用模拟数据。这可确保您的测试在整个更改过程中保持一致,因此可以依赖。
您还应该记住让您的测试数据库在功能上与您的实时数据库相同,以避免落后和过时的测试。

这里的关键是:抽象您的数据源层。如果你把所有的数据库调用放在一个接口后面,你可以简单地模拟/存根这个接口并从那里提供测试数据。

您的工作单元应该切中要害,并测试该方法应该做的一件事(单一职责原则)。
如果您的方法的目标是操作数据库中的数据,那么您可以模拟您的数据源并对您的操作进行单元测试。
如果您的方法的目标是调用数据库中的存储过程,那么您可以使用集成测试来提供过程输入并针对其输出进行断言。

这个原则的一个简单例子:

interface DataSource {
    List<String> GetData();
}

class Manipulator {
    private DataSource _source;

    public Manipulator() { }

    public Manipulator(DataSource d) { _source = d; }

    public void ManipulateData() {
        var data = _source.GetData();

        // Do something with the data
    }
}

此示例使用构造函数注入(查看依赖注入和 IoC 容器的概念!)。

现在回答你的问题:

如果我必须从我的单元测试中调用这些方法,我实际上是在进行 n 次数据库调用...值得吗?

是的,否则您没有集成测试。由于您有一个单独的测试数据库,这不会干扰您的生产环境。

如果我必须使用模拟数据,如何从本地数据库中获取数据的副本?

说实话,我不太确定你在这里问的是什么,但我认为可以通过测试数据库的想法来解决。

如何模仿存储过程调用来使用模拟数据?

你没有。您可以通过使用测试数据调用它们并根据您的期望断言结果来使用实际的存储过程。如果他们工作了,那就结束了,如果他们没有返回正确的结果,那么你就知道出了问题。

【讨论】:

  • 非常感谢您的详细解释!我们有相同数据库的不同副本……比如 100 个。数据在某些情况下可能不同,但在某些情况下相同。在这种情况下,更好的方法是什么?
  • 为什么有 100 个数据库?它们是功能相同的数据库,只是数据不同,还是数据库完全不同?
  • 是的!在大多数情况下,它都是相同的架构,但数据不同......出于数据保护和其他原因,它是单独维护的。谢谢!
  • 那么你只需要一个测试数据库。您想测试数据库中的功能(存储过程),而不是数据本身。因此,现在您将拥有 101 个数据库:100 个包含生产数据,1 个包含测试数据,您可以在其中执行测试。
【解决方案2】:

您需要定义一个interface,它包含您所有的数据访问方法。您已经有一个具有这些方法的类,因此您可以在 Visual Studio 中通过右键单击数据访问类中的空白区域并选择 Refactor 然后选择 Extract Interface来重构它>。所以现在你有了一个interface,你需要添加一个模拟数据访问类来实现interface。

在执行此操作时,您的模拟数据访问类不需要了解任何存储过程,只要它返回由interface 指定的正确类型的一些数据。最后一部分是在使用应用程序时传递实际数据访问类的实例,而为了进行测试,则改为传递模拟数据访问类的实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 2020-07-03
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    相关资源
    最近更新 更多