代码之家  ›  专栏  ›  技术社区  ›  kdmurray

如何为数据库调用编写单元测试

  •  59
  • kdmurray  · 技术社区  · 15 年前

    我马上就要开始一个新项目了,(喘气!)我第一次尝试在我的项目中包含单元测试。

    我无法自行设计一些单元测试。我有一些方法非常容易测试(通过两个值并检查预期的输出)。我还有代码的其他部分在做更复杂的事情,比如对数据库运行查询,我不知道如何测试它们。

    public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters)
    {
        DataTable resultSet = new DataTable();
        SqlCommand queryCommand = new SqlCommand();
        try
        {
            queryCommand.Connection = ActiveConnection;
            queryCommand.CommandText = Query;
    
            if (Parameters != null)
            {
                foreach (SqlParameter param in Parameters)
                {
                     queryCommand.Parameters.Add(param);
                }
            }
    
            SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand);
            queryDA.Fill(resultSet);
        }
        catch (Exception ex)
        {
            //TODO: Improve error handling
            Console.WriteLine(ex.Message);
        }
    
        return resultSet;
    }
    

    这个方法本质上接受了从数据库中提取一些数据所必需的所有位和块,并返回DataTable对象中的数据。

    第一个问题可能是最复杂的:在这种情况下,我应该测试什么?

    一旦解决了这一问题,问题就来了:是模拟数据库组件,还是尝试根据实际的数据库进行测试。

    9 回复  |  直到 6 年前
        1
  •  41
  •   Community CDub    8 年前

    你在测试什么?

    有三种可能,从我的头顶上:

    a.您正在测试DAO(数据访问对象)类,确保它正确地封送传递到数据库的值/参数,并正确地封送/转换/打包数据库中得到的结果。

    在这种情况下,您根本不需要连接到数据库;您只需要一个单元测试,用一个模拟替换数据库(或中间层,如JDBC,(n)Hibernate,iBatis)。

    B.您正在测试(生成的)SQL的语法正确性。

    在这种情况下,由于SQL方言不同,您希望针对RDBMS的正确版本运行(可能生成)SQL,而不是试图模拟RDBMS的所有特性(并使更改功能的任何RDBMS升级都被您的测试捕获)。

    C.您正在测试 语义的 您的SQL的正确性,即对于给定的基线数据集,您的操作(访问/选择和突变/插入和更新)将生成预期的新数据集。

    为此,您需要使用DBUnit之类的工具(它允许您设置基线并将结果集与预期结果集进行比较),或者使用我在这里概述的技术,完全在数据库中进行测试: Best way to test SQL queries .

        2
  •  24
  •   MusiGenesis    15 年前

    这就是为什么(imho)单元测试有时会对开发人员造成错误的安全感。根据我对与数据库对话的应用程序的经验,错误通常是数据处于意外状态(异常或缺少值等)的结果。如果您经常在单元测试中模拟数据访问,那么当您的代码实际上仍然容易受到这种错误的影响时,您会认为它工作得很好。

    我认为您最好的方法是手边有一个测试数据库,其中充满了糟糕的数据,并针对它运行您的数据库组件测试。同时要记住,你的用户在处理数据方面要比你强得多。

        3
  •  9
  •   Jörg W Mittag    15 年前

    单元测试的关键是 测试单元 (duh)隔离。数据库调用的关键是 整合 使用另一个单元(数据库)。Ergo:单元测试数据库调用没有意义。

    但是,您应该调用集成测试数据库(如果需要,您可以使用与单元测试相同的工具)。

        4
  •  6
  •   Arafangion    9 年前

    为了上帝的爱,不要在一个活生生的,已经填充的数据库上进行测试。但你知道。

    一般来说,您已经知道了每个查询将检索哪种类型的数据,无论您是对用户进行身份验证,还是查找电话簿/组织图表条目,或者其他什么。你知道你对哪些领域感兴趣,也知道它们有哪些限制(例如, UNIQUE , NOT NULL 等等。您是在单元测试与数据库交互的代码,而不是数据库本身,因此请考虑如何测试这些函数。如果一个领域 NULL ,您应该有一个测试来确保您的代码能够处理 无效的 值正确。如果其中一个字段是字符串( CHAR , VARCHAR , TEXT 测试以确保正确处理转义字符。

    假设用户将尝试将任何*放入数据库,并相应地生成测试用例。您将要为此使用模拟对象。

    *包括不受欢迎、恶意或无效的输入。

        5
  •  4
  •   Michu93    6 年前

    您可以对所有内容进行单元测试,除了: queryDA.Fill(resultSet);

    一旦你执行 queryDA.Fill(resultSet) 您要么模仿/伪造数据库,要么正在进行集成测试。

    我认为,不认为集成测试是坏的,只是它会捕获不同类型的错误,有不同的假阴性和假阳性的几率,不太可能经常进行,因为它太慢了。

    如果我正在单元测试这段代码,我将验证参数是否正确生成,命令生成器是否创建了正确数量的参数?它们都有价值吗?是否正确处理空字符串、空字符串和dbnull?

    实际上,填充数据集是在测试数据库,它是DAL范围之外的一个脆弱组件。

        6
  •  3
  •   azheglov    15 年前

    严格来说,从数据库或文件系统中写入/读取的测试不是单元测试。(尽管它可能是一个集成测试,并且可以使用nunit或junit编写)。单元测试应该测试单个类的操作,隔离它的依赖关系。因此,当您为接口和业务逻辑层编写单元测试时,根本不需要数据库。

    好的,但是如何对数据库访问层进行单元测试?我喜欢这本书的建议: xUnit Test Patterns (链接指向本书的“测试w/db”章节。关键是:

    • 使用往返测试
    • 不要在数据访问测试夹具中编写太多测试,因为它们的运行速度要比“实际”单元测试慢得多。
    • 如果可以避免使用真正的数据库进行测试,则可以在没有数据库的情况下进行测试
        7
  •  1
  •   tvanfosson    15 年前

    对于单元测试,我通常模拟或伪造数据库。然后通过依赖注入使用模拟或假的实现来测试方法。您可能还拥有一些集成测试,这些测试将测试数据库中的约束、外键关系等。

    至于要测试的内容,请确保该方法使用来自参数的连接,将查询字符串分配给命令,并且返回的结果集与通过对fill方法的预期提供的结果集相同。注意——测试返回值的get方法可能比修改参数的fill方法更容易。

        8
  •  1
  •   Community CDub    8 年前

    为了正确地做到这一点,您应该使用一些依赖注入(DI),而对于.NET,有几个依赖注入。我目前正在使用统一框架,但还有一些更简单。

    这里有一个关于这个主题的链接,但是还有其他链接: Dependency Injection in .NET with examples?

    这将使您能够更容易地模拟应用程序的其他部分,只需让一个模拟类实现接口,这样您就可以控制它将如何响应。但是,这也意味着要设计一个接口。

    既然你问过最佳实践,我想这就是最佳实践。

    然后,除非你需要,否则不要去数据库,这是另一个建议。

    如果您需要测试某些行为,例如具有级联删除的外键关系,那么您可能需要为此编写数据库测试,但通常不到真正的数据库是最好的,特别是因为一次可能会有多个人运行单元测试,如果他们要到相同的数据库测试,则可能会失败,因为预期的数据可能会发生更改。

    编辑:我指的是数据库单元测试,因为它的设计目的是只使用T-SQL进行一些设置、测试和拆卸。 http://msdn.microsoft.com/en-us/library/aa833233%28VS.80%29.aspx

        9
  •  0
  •   cchantep    6 年前

    在基于JDBC的项目上,可以模拟JDBC连接,这样就可以在没有活动RDBMS的情况下执行测试,并且每个测试用例都是独立的(没有数据冲突)。

    它允许验证持久性代码传递正确的查询/参数(例如 https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala )并按预期处理JDBC结果(解析/映射)(“获取从数据库中提取某些数据所需的所有位和块,并返回数据表对象中的数据”)。

    框架(如jooq或my framework acolyte)可用于: https://github.com/cchantep/acolyte .