2

首先,我使用Scala,但任何Java方法都可能有效。

我有一个带有数据库连接的应用程序,我想在不修改数据库或数据库脱机的情况下运行我的测试并开发我的应用程序。

说,我有一个连接到数据库的大类(或模块),做所有你想做的事情,我如何从外部访问该类或其参数?

例如,如果我希望该类正常运行,而不是statement.executeUpdate( sql ),我想要一个println( "Did: " + sql ), 而不是在此测试中调用第一个方法。

显然,一种方法是简单地替换这些语句 - 或复制整个文件并替换它们。但它很容易出错,如果我把它改回来,我可能会忘记一些东西。另外,它非常多余。

如何解决这个问题?怎么办JUnit

免责声明:请不要使用“参数化您的课程”之类的解决方案。我希望我的构造函数有很少的参数,我不想在调用它时指定所有内容。测试类在我的应用程序中是二等公民,它们对实际类/实际开发几乎没有影响。

4

4 回答 4

5

您应该定义一个包含类中所有数据库方法的接口。然后,确保您现有的数据库类实现该接口。

现在您有了一个接口,您可以模拟该类或开发一个存根类进行测试。存根类可以只打印 SQL 或任何您想要的。模拟类更强大,可用于确保您的业务逻辑正常工作。

最后一步是确保使用数据库的任何地方都在构造函数中接受您的接口。例如

public class ClassThatUsesTheDatabase {

    public ClassThatUsesTheDatabase(DatabaseProvider provider) {
      //...
    }
}

DatabaseProvider你的界面在哪里。这允许您ClassThatUsesTheDatabase使用存根或模拟进行测试。在生产中,您将使用您的具体实现来构建此类。

在我看来,这是编写依赖外部资源的应用程序的唯一明智的方法。


重新阅读您的问题后,我担心以下段落:

免责声明:请不要使用“参数化您的课程”之类的解决方案。我希望我的构造函数有很少的参数,我不想在调用它时指定所有内容。测试类在我的应用程序中是二等公民,它们对实际类/实际开发几乎没有影响。

测试班不是二等公民。如果您认真对待代码质量,它们与您的生产代码一样重要。是的,您经常必须设计生产代码以使其适合单元测试。这是不可避免的。然而,好处是巨大的。

于 2013-02-24T08:20:01.313 回答
4

请不要使用“参数化您的课程”之类的解决方案。

那么你做错了。您的数据库调用应该在易于用虚拟实现替换的模拟对象中。很有可能,如果您仍然使用 executeStatement,那么您很容易受到 SQL 注入的攻击。你应该重新考虑你的方法。

于 2013-02-24T08:02:37.993 回答
1

就我个人而言,我支持 Bob 大叔,因为您希望所有 DB 交互都被隔离到程序中的一个类中,这样您就可以在测试中用其他东西替换它。如果你不想参数化,还有蛋糕模式和依赖注入。

现在,如果您坚持对与数据库对话的库的依赖项进行硬编码,您仍然可以做一件事:模拟数据库。这可能很难做到,因为您必须实现数据库接口。

我实际上是在一个项目中这样做的,尽管它是一个基于 HTTP+JSON 的 NoSQL 数据库,所以模拟数据库就像模拟 Web 服务器一样简单——只需几十行未过滤的代码即可产生所有所需的响应。

然后只需在测试期间指向假数据库,就可以了。

尽管如此,使代码更容易测试可以使代码更好。

于 2013-02-24T08:23:42.297 回答
0

使用 JUnit(和任何其他主流单元测试框架),标准的解决方案范围是某种依赖注入,这涉及到您所说的“参数化您的类”。没有王道,抱歉 :-) 但是,您可以通过各种方式来最小化或避免生产代码的中断。

处理这类问题的常用方法是将要测试的逻辑与数据库访问代码分离,方法是提取后者并将其隐藏在可模拟接口后面。然后在被测代码中,语句 likestatement.executeUpdate(sql)被替换为调用 like dataAccessor.updateFooBar(foo, bar)。并且执行这些调用的数据访问器对象以某种方式注入到您的类中 - 它可能是一个可选的构造函数参数,它可能是一个默认值是真实数据访问器对象但它有一个具有包访问权限的设置器来设置单元测试期间的模拟数据访问器...

于 2013-02-24T08:22:35.217 回答