任何关于如何在 PHPUnit 中使用 setUp() 和 tearDown() 的真实例子?

Posted

技术标签:

【中文标题】任何关于如何在 PHPUnit 中使用 setUp() 和 tearDown() 的真实例子?【英文标题】:Any real word example about how setUp() and tearDown() should be used in PHPUnit? 【发布时间】:2012-12-06 05:43:06 【问题描述】:

方法setUp()tearDown() 在每次测试之前和之后被调用。但说真的,有什么真实的例子说明我为什么需要这个?

检查其他人的测试,我总是看到类似的东西:

public function setUp()

    $this->testsub = new TestSubject();


public function tearDown()

    unset($this->testsub);


public function testSomething()

    $this->assertSame('foo', $this->testsub->getFoo());

当然,这种方式与“旧”的局部变量方式几乎没有区别。

【问题讨论】:

不同的是,你只需要设置一次代码,不管你的测试类中有多少测试 @MarkBaker so...只是在创建测试主题时需要很多行,例如? 一个很好的例子是建立一个数据库表并在完成测试时再次销毁它。 @Gremo -- setUp 和 tearDown 为类中的 every 测试方法运行。因此,如果您更改对象的变量,您需要在下一次测试中使用新对象 我在 30-50 个测试课程的实际场景中使用它 【参考方案1】:

如果您单独执行每个测试方法,您的测试代码将共享很多行,这些行只是创建要测试的对象。此共享代码可以(但不应该)进入 setup 方法。

创建要测试的对象所需执行的任何操作也会进入 setup 方法,例如创建注入测试对象的构造函数的模拟对象。

这一切都不需要拆除,因为下一次调用 setup 将使用一组新对象初始化类成员变量。

唯一需要拆卸的是您的测试是否会永久留下某些内容,例如创建的文件或数据库条目。编写执行此类操作的测试确实不是一个好主意,但在某些时候,您无法再抽象,不得不接触硬盘、数据库或真实网络之类的东西。

因此,除了需要拆卸之外,还有很多设置,如果此测试没有工作要做,我总是删除拆卸方法。

关于模拟,我是这样工作的:

private $_mockedService;
private $_object;
protected function setUp()

    $this->_mockedService = $this->getMock('My_Service_Class');
    $this->_object = new Tested_Class($this->_mockService);


public function testStuff()

    $this->_mockedService->expects($this->any())->method('foo')->will($this->returnValue('bar'));
    $this->assertEquals('barbar', $this->_object->getStuffFromServiceAndDouble());

【讨论】:

tearDown() 这里缺少,但基本上你会取消设置任何需要清除的东西,类似于析构函数。 @StevenScott 不,teardown() 故意不在我的代码中,因为setUp() 无论如何都会覆盖任何内容。无需编写将NULL 写入该测试类的任何私有属性的拆卸函数。 @Sven 是的,tearDown() 是多余的,因为 setUp() 将创建一个新对象。但是,我仍然倾向于使用 tearDown() 来清除对象,因此垃圾收集可能会更快启动,并且在大型代码库上效率更高。另外,记得清理我创建的任何代码(甚至是测试)对开发人员来说是一个好习惯。【参考方案2】:

您可以实例化一堆夹具对象,并在每个测试中将它们用作实例变量,而不是为每个测试单独构造它们。

您可以在 setUp 中创建文件句柄等资源,然后确保在 tearDown 中关闭它们。如果您正在编写临时文件,则可以确保删除它们。如果您打开一个数据库连接,您可以关闭它(尽管您可能想在其他地方这样做 - setupBeforeClass / tearDownAfterClass 会为每个测试文件调用,而不是每个测试用例。)

这只是一个之前/之后的钩子,一般来说这是一件很棒的事情。使用它让您的生活更轻松,或者不要使用它。

【讨论】:

+1 表示“使用它让您的生活更轻松,或者不要使用它。” setUp() 只是一个重构工具,用于共享common 代码。仅在有意义时使用它;不要强迫所有的夹具创建都进去,因为每个测试都需要一个稍微不同的夹具!【参考方案3】:

在接受的答案中提供的示例中存在内存泄漏。 你应该添加tearDown:

protected function tearDown()

    $this->_mockedService = null;

phpUnit 为每个测试方法调用创建新的测试用例对象。因此,如果有 4 个测试方法 - 将有 4 个对象,并且将创建 4 个 mockedService。直到脚本结束(整个测试套件),它们才会被删除。 所以需要在 tearDown 中删除所有对象并取消设置所有变量以防止内存泄漏。

【讨论】:

【参考方案4】:

您几乎可以在您正在测试的类中有依赖项的任何时候使用它。一个典型的例子可能是某种存储应用程序状态的对象(会话对象、购物车等)。

例如,我有一个类要计算购物车对象定义的购物车内容的运费。假设这个购物车通过依赖注入传递到运输计算类。要测试类的大多数方法,您可能需要实际实例化一个购物车对象并将其设置在类中,以便对各种方法进行单元测试。您可能还需要将商品添加到购物车中。所以你可能会有这样的设置:

public function setUp()

    $this->cart = new cart();
    $this->cart->add_item('abc');
    $this->cart->add_item('xyz');

我们还假设您的测试方法实际上可能会修改购物车的商品,并用运费信息装饰它们。您不希望从一个测试中获取信息流到下一个测试中,因此您只需在最后取消设置购物车。

public function tearDown()
    unset($this->cart);

【讨论】:

您需要实例化购物车的 MOCK,它将仅返回您在类中测试计算所需的值。设置一个真正的购物车可能太复杂了,因为它自己的依赖关系,你无法测试是否使用正确的参数和方法调用了购物车。 @Sven 我同意模拟推车实际上是最好的。我试图简单地说明一个用例,它可以让您了解setUp()tearDown() 的实用性,而无需引入更高级的单元测试概念。也许我没有在这里选择最好的例子:)

以上是关于任何关于如何在 PHPUnit 中使用 setUp() 和 tearDown() 的真实例子?的主要内容,如果未能解决你的问题,请参考以下文章

PHPUnit:从setUp()获取测试类和方法的名称?

PHPUnit -setUp() - 它在每个测试用例之前和之后运行吗?

如何检测测试套件和组?

如果重写setUp和tearDown,则不会调用PHP,phpunit和dbunit - getConnection和getDataSet

PHPUnit测试和Doctrine,连接太多

phpunit 8 及更高版本中设置套件的问题