首先,我使用Scala
,但任何Java
方法都可能有用。
我有一个带数据库连接的应用程序,我想运行我的测试并开发我的应用程序,而无需修改数据库或数据库是否脱机。
说,我有一个连接数据库的大类(或模块),做你想做的所有事情,我怎样才能从外面访问那个类或它的参数?
例如,如果我希望该类正常运行但不是statement.executeUpdate( sql )
,我想要一个println( "Did: " + sql )
,在此测试中没有调用第一种方法。
显然,一种方法是简单地替换这些语句 - 或复制整个文件并替换它们。但这很容易出错,如果我改回来,我可能会忘记一些事情。此外,它非常多余。
如何解决这个问题?如何使用JUnit
?
免责声明:请不要使用“参数化课程”等解决方案。我希望我的构造函数只有很少的参数,我不想在调用它时指定所有内容。测试类在我的应用程序中是二等公民,它们对实际的类/实际开发几乎没有影响。
答案 0 :(得分:5)
您应该定义一个包含类中所有数据库方法的接口。然后,确保您现有的数据库类实现该接口。
现在您有了一个接口,您可以模拟该类或开发一个存根类进行测试。存根类只能打印SQL或任何你想要的。模拟类功能更强大,可用于确保您的业务逻辑正常运行。
最后一步是确保使用数据库的任何地方都接受构造函数中的接口。 E.g。
public class ClassThatUsesTheDatabase {
public ClassThatUsesTheDatabase(DatabaseProvider provider) {
//...
}
}
其中DatabaseProvider
是您的界面。这允许您使用存根或模拟测试ClassThatUsesTheDatabase
。在生产中,您将使用具体实现来构造此类。
在我看来,这是编写依赖外部资源的应用程序的唯一理智方式。
重新阅读你的问题之后,我关注以下段落:
免责声明:请不要使用“参数化课程”等解决方案。我希望我的构造函数只有很少的参数,我不想在调用它时指定所有内容。测试类在我的应用程序中是二等公民,它们对实际的类/实际开发几乎没有影响。
考试班不是二等公民。如果您对代码质量非常认真,它们与您的生产代码一样重要。是的,您经常需要设计生产代码,使其适合单元测试。这是不可避免的。然而,好处是巨大的。
答案 1 :(得分:4)
请不要使用“参数化你的课程”等解决方案。
然后你做错了。您的数据库调用应该在模拟对象中,这些对象很容易用虚拟实现替换。有可能,如果您正在使用executeStatement,那么您很容易受到SQL注入攻击。你应该重新考虑你的方法。
答案 2 :(得分:1)
就我个人而言,我和Bob叔叔在一起,因为你希望所有数据库交互都被分离到程序中的一个类中,这样你就可以用其他东西替换它。如果你不想参数化,那么仍然存在蛋糕模式和依赖注入。
现在,如果你坚持对与数据库对话的库进行硬编码,那么你仍然可以做一件事:模拟数据库。由于必须实现数据库接口,这可能很难做到。
我实际上是在一个项目上做到了,虽然它是一个基于HTTP + JSON的NoSQL数据库,因此模拟数据库就像模拟一个Web服务器一样简单 - 只需几十行Unfiltered代码来产生所有想要的响应
然后在测试过程中指向假数据库,你就可以了。
尽管如此,让代码更容易测试可以使代码更好。
答案 3 :(得分:0)
使用JUnit(以及任何其他主流单元测试框架),标准的解决方案范围是某种依赖注入,其中包括在您调用它时“参数化您的类”。没有皇室之路,抱歉:-)但是,您可以通过各种方式实现这一目标,以最大限度地减少或避免生产代码中断。
处理这些问题的常用方法是通过提取后者并将其隐藏在可模拟接口后面,将要测试的逻辑与数据库访问代码分开。然后在测试代码中,statement.executeUpdate(sql)
之类的语句被dataAccessor.updateFooBar(foo, bar)
之类的调用所取代。并且执行这些调用的数据访问器对象以某种方式注入到您的类中 - 它可能是一个可选的构造函数参数,它可能是一个字段,其默认值是真正的数据访问器对象,但它有一个具有包访问权限的setter,可以设置单元测试期间的模拟数据访问器......