我正在构建一个 Web 应用程序,该应用程序将数据库用于用户、安全/角色并存储内容。
开始单元测试的道路对我来说似乎有点令人生畏,因为我必须确保我的数据库已正确初始化,以便我的测试运行。
在这方面有哪些常见的做法可以提供帮助?
即在开发/测试时,我可能会删除一个用户,但要让我的测试通过,该用户必须在数据库中,以及他的个人资料、安全设置等。
我知道我可以创建一个设置脚本,重新创建数据库等。
我不想最终花费我的全部时间来维护我的测试并确保我的数据库处于同步状态
或者这就是单元测试/TDD 的成本?
我正在构建一个 Web 应用程序,该应用程序将数据库用于用户、安全/角色并存储内容。
开始单元测试的道路对我来说似乎有点令人生畏,因为我必须确保我的数据库已正确初始化,以便我的测试运行。
在这方面有哪些常见的做法可以提供帮助?
即在开发/测试时,我可能会删除一个用户,但要让我的测试通过,该用户必须在数据库中,以及他的个人资料、安全设置等。
我知道我可以创建一个设置脚本,重新创建数据库等。
我不想最终花费我的全部时间来维护我的测试并确保我的数据库处于同步状态
或者这就是单元测试/TDD 的成本?
解决方案是模拟。模拟“替换”连接。被测单元将“连接”到 Mock 并执行其语句。Mock 返回正常的结果集
测试后,mock 可以为您提供所有方法的列表,这些方法由被测单元调用。Easymock.org
正如另一个所说:数据库连接不是单元测试。所以放下它,用 Mocking 对象在本地做
如果您要测试多个单元,则它不是单元测试。
通常,您将有一个组件(您的页面或业务层)与负责实际连接和查询数据库的数据层对象通信。我的建议是为第一个组件开发一个单元测试,使用依赖注入来传入 DataLayer 的模拟版本(它作用于硬编码的数据,或者你传入的 List 等)。这样,您就可以在与其他组件隔离的情况下测试更高级别的代码。
然后,您可以自由地为数据层开发其他单元测试(和集成测试),以确保它正确处理其工作(写入数据库)。
我们使用内存数据库 (hsqldb) 进行单元测试。在设置中,我们用测试数据填充它,然后在每个测试用例之前我们开始一个事务,在每个测试用例之后我们将它回滚。这意味着每个测试用例都有一个干净的数据库启动。
听起来您实际上想要功能/集成测试。对于 Web 应用程序,我建议您研究Selenium或Canoo WebTest。
这些也非常适合自动化您在 Web 上执行的任务。我有一个设置套件和一个拆卸套件,它们可以创建业务实体并通过管理界面测试用户以及面向客户的站点的测试。
Michael Feathers认为,与数据库通信的测试不是定义上的单元测试。这样做的主要原因是您提出的观点:单元测试应该简单且易于运行。
这并不是说您不应该测试数据库代码。但是您不想考虑它们的单元测试。因此,如果您进行任何数据库测试,您希望将测试与其余的单元测试分开。
由于我使用 Doctrine 进行我的 PHP 数据库工作,并且由于 Doctrine 有一个查询抽象层(称为 DQL),我可以换掉后端而不必过多担心兼容性问题。因此,在这种情况下,对于我的单元测试,我会在测试开始时将架构和固定装置加载到 SQLlite 数据库中,测试我的模型,并在测试结束时丢弃 SQLlite 数据库。
通过这种方式,我测试了我的模型和数据访问,以确保它们的查询格式正确。
现在测试特定的数据库实例以确保当前模式正确是另一回事,恕我直言,它可能不属于您的单元测试,就像它属于您的部署任务列表一样。
单元测试/TDD 的代价是你必须改变你的设计,以便在数据库和领域层之间有一个清晰的分离,这样你就可以创建一个假的,允许你创建不会命中的测试数据库。
但更清洁的设计只是成本的开始。之后,您必须创建测试,既可以帮助您使代码在第一时间正常工作,又可以在有人破坏以前可以正常工作的东西时提醒您。
在您拥有一个良好的基础设计并通过测试保护您现有的功能之后,您会发现自己正在清理代码以使其更易于使用,并且确信您不会在此过程中破坏任何东西。
等等等等……随着时间的推移,单元测试/TDD 的成本不断增加。
对于 Java,您可能还想查看 dbunit。http://www.dbunit.org/