使用Symfony 2和数据库进行单元测试

时间:2014-02-05 15:16:43

标签: php sql unit-testing symfony

我正在尝试了解涉及数据库时的单元测试。我的课程很大程度上依赖于数据库来完成他们的工作。我已经在stackoverflow和一般的互联网上看了很多答案,但我对我发现的东西并不满意。

当涉及数据库时(我的情况是MySQL),我看到的一个常见问题是测试真的很长,而且一种可能的解决方案是使用SQLite。

我无法在测试中的任何地方使用它,因为有些类使用RAW查询而不是Doctrine querybuilder来绕过未知日期时间函数的限制。 SQLite对日期值有一组不同的函数,这意味着要在测试中使用它,我应该重写我的查询两次,一次用于MySQL,一次用于SQLite。

我决定坚持使用MySQL,但每次测试运行时创建和删除模式需要花费很多时间。我知道SchemaTool类可以处理模式子集的创建:如果只创建我在测试套件中真正需要的表,或者我应该总是使用完整的模式,那么这将是一个很好的解决方案吗?

代码部分示例(伪代码)我正在尝试测试

NotificationManagerClass
    constructor(EntityManager)
    loadNotifications()
    deleteNotification()
    updateNotification()

如您所见,我将实体管理器注入到类的构造函数中。该类是在Symfony容器中注册的服务。然后在控制器中我获得此服务并使用其方法。在方法内部,我使用querybuilder,因为我必须能够访问其他一些服务,并且存储库不是容器感知的。我怎样才能解除比我班级更多的问题呢?我无法想办法做到这一点

2 个答案:

答案 0 :(得分:4)

你把很多单词混合在一起,这些单词并不都具有你给他们的意思。快速摘要的单词:

  • 单元测试 - 仅测试一个类。它不测试也不实例化任何其他类(它使用模拟或存根)。它不应该使用数据库,而应该是模拟学说。
  • Web测试 - 这些测试测试多个类如何使用数据库协同工作。这些通常很慢,所以你不想在这里进行很多非常具体的测试。
  • MySQL SQLite 都只是数据库驱动程序。 SQLite和MySQL一样慢,你使用哪一个并不重要(好吧,它确实很重要,你必须使用与你在生产中使用相同的驱动程序)。

你不能模仿一切,因为你决定使用原始的mysql函数(糟糕的选择)...总是避免在类中使用mysql查询。使用Doctrine(有很多简单的方法来绕过未知的日期时间函数)或将原始查询放在Repository中并模拟存储库。

简而言之:

  • 在单元测试中不要使用测试对象以外的任何东西(mock / stub其他)
  • 网络测试应该使用生产工具
  • 不要直接在类中使用mysql

答案 1 :(得分:2)

涉及数据库的测试不是单元测试。您的存储库应该使用集成或功能测试进行测试(如果您这样做,则接受测试)。

如果您有许多涉及数据库的测试,这可能意味着您在某处出现了错误的转弯:

  • 代码设计 - 您的类与数据库耦合
  • 决定如何测试 - 编写太多集成测试而不是单元测试

可能值得研究测试金字塔:http://martinfowler.com/bliki/TestPyramid.html

通过研究如何加快测试速度,你只能继续走错路。解决方案是编写更多的单元测试和更少的集成测试。单元测试快速,独立且隔离,因此难以破解。集成测试更加脆弱和缓慢。查看FIRST properties of unit tests

顺便说一句:SQLite是一个死胡同。它的行为与mysql不同,因为它不支持约束。

您的示例类可以建模为:

class NotificationManager
{
    public function __construct(MyNotificationRepository $repository, MyFabulousService $service)
    {}

    // ... other methods
}

通过这种方式,您可以注入存储库和服务的假货,并单独测试NotificationManager。

单元测试查询构建器没有多大价值,因为您要测试的是doctrine而不是代码。它也很难,因为查询类被声明为final。您可以测试查询是否在功能上返回正确的结果。如果你正确地测试你的课程,那么所需的功能测试就会少得多。

如果你将MyNotificationRepository作为一个界面,你甚至可以从教义中解脱出来。

相关问题