在单元测试中模拟 PHP 函数

发布于 2024-07-30 05:44:35 字数 473 浏览 3 评论 0原文

我正在使用 SimpleTest 对一些 PHP 代码进行单元测试,但遇到了麻烦。 在我对数据库类的测试中,我希望能够为 PHP 的 mysql 函数设置期望。 在我对 mail 函数的包装类的测试中,我想模拟 PHP 的 mail 函数。 这些只是一些例子。

要点是:我不想(总是)测试我的 Mail 类是否发送电子邮件,我想测试它如何调用 mail 函数。 我希望能够控制这些函数返回的内容。 我希望能够测试我的数据库类,而不需要数据库、固定装置等等。

我在测试 Ruby 代码方面有一些经验,Test::Unit 和 RSpec 使单独测试代码变得非常容易。 我是测试 PHP 的新手,为了让我的测试通过,我感觉我正在测试比我应该需要的更多的东西。

SimpleTest 或 PhpUnit 或其他测试框架是否有办法使这成为可能或更容易?

I'm unit-testing some PHP code with SimpleTest and I've run into trouble. In my tests of a database class I want to be able to set an expectation for PHPs mysql functions. In my tests of a wrapper class for the mail function I want to mock PHPs mail function. These are just some examples.

The point is: I don't (always) want to test if my Mail class sends e-mail, I want to test how it calls the mail function. I want to be able to control what these functions return. I want to be able to test my Database class without needing a database, fixtures and that whole lot.

I've got some experience with testing Ruby code, and Test::Unit and RSpec make it very easy to test code in isolation. I'm new to testing PHP and it feels like I'm testing a lot more than I should need to, in order to get my tests to pass.

Is there a way in SimpleTest or PhpUnit or some other testing framework that makes this possible or easier?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绅刃 2024-08-06 05:44:36

不是以自动化的方式。 您可以做的就是以一种将外部依赖项包装在从外部传入的对象中的方式编写代码。 在生产环境中,您只需连接真正的适配器,但在测试过程中,您可以将其连接到存根或模拟。

如果你真的坚持,你可以使用runkit扩展来改变php的编程模型,以便您可以在运行时重新定义类和函数。 然而,这是一个外部的非标准扩展,所以请记住这一点。 正如我上面所描述的,事实上的标准是一种手动方法。

Not in an automated way. What you can do, is to write your code in a way such that external dependencies are wrapped in objects that are passed in from the outside. In your production environment you'll just wire up the real adapters, but during testing, you can wire it to stubs or mocks.

If you really insist, you can use the runkit extension which changes php's programming model so that you can redefine classes and functions at runtime. This is an external and nonstandard extensions however, so keept that in mind. The defacto standard is a manual approach as I described above.

孤单情人 2024-08-06 05:44:36

这是一篇有趣的文章,其中写了有关模拟全局 php 函数的内容。 作者提出了一个非常有创意的解决方案,通过覆盖 SUT(被测服务)命名空间内的方法来“模拟”(类似)全局 php 函数。

这里的代码来自博客文章中的一个示例,其中模拟了 time 函数:

<?php

namespace My\Namespace;

use PHPUnit_Framework_TestCase;

/**
 * Override time() in current namespace for testing
 *
 * @return int
 */
function time()
{
    return SomeClassTest::$now ?: \time();
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var int $now Timestamp that will be returned by time()
     */
    public static $now;

    /**
     * @var SomeClass $someClass Test subject
     */
    private $someClass;

    /**
     * Create test subject before test
     */
    protected function setUp()
    {
        parent::setUp();
        $this->someClass = new SomeClass;
    }

    /**
     * Reset custom time after test
     */
    protected function tearDown()
    {
        self::$now = null;
    }

    /*
     * Test cases
     */
    public function testOneHourAgoFromNoon()
    {
        self::$now = strtotime('12:00');
        $this->assertEquals('11:00', $this->someClass->oneHourAgo());
    }
    public function testOneHourAgoFromMidnight()
    {
        self::$now = strtotime('0:00');
        $this->assertEquals('23:00', $this->someClass->oneHourAgo());
    }
}

不确定这是否是一个好方法,但它确实工作得很好,我认为这里值得一提。 可能是一些讨论的食物......

Here is an interesting article that writes about mocking global php functions. The author proposes a very creative solution to 'Mock' (kind off) the global php functions by overwriting the methods inside the namespace of the SUT (service under test).

Here code from an example in the blog post where the time function is mocked:

<?php

namespace My\Namespace;

use PHPUnit_Framework_TestCase;

/**
 * Override time() in current namespace for testing
 *
 * @return int
 */
function time()
{
    return SomeClassTest::$now ?: \time();
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var int $now Timestamp that will be returned by time()
     */
    public static $now;

    /**
     * @var SomeClass $someClass Test subject
     */
    private $someClass;

    /**
     * Create test subject before test
     */
    protected function setUp()
    {
        parent::setUp();
        $this->someClass = new SomeClass;
    }

    /**
     * Reset custom time after test
     */
    protected function tearDown()
    {
        self::$now = null;
    }

    /*
     * Test cases
     */
    public function testOneHourAgoFromNoon()
    {
        self::$now = strtotime('12:00');
        $this->assertEquals('11:00', $this->someClass->oneHourAgo());
    }
    public function testOneHourAgoFromMidnight()
    {
        self::$now = strtotime('0:00');
        $this->assertEquals('23:00', $this->someClass->oneHourAgo());
    }
}

Not sure if this is a good way to do it but it surely works well and I think it is worth mentioning here. Could be some food for a discussion...

伴我老 2024-08-06 05:44:36

在 PHP 5.3+ 环境中,您可以通过破解命名空间来解决使用 runkit 扩展的需要。 唯一的要求是函数调用不使用像 \mysql_query() 这样的完全限定的命名空间 - 而且通常不会。 然后,您可以通过在命名空间中定义测试来在测试中定义相同的函数,PHP 将调用您的函数而不是全局函数。 我个人使用这种方法来存根 time() 函数调用。 这是一个使用模拟框架的好例子

In PHP 5.3+ environment you may workaround the need to use runkit extension by hacking the namespaces. The only requirement in that the function calls do not use fully-qualified namespace like \mysql_query() - and they usually don't. Then you can define the same function in your test, by defining the test in a namespace, and PHP will call your function instead of the global one. Personally I use this approach to stub the time() function call. Here is a nice example with the mockery framework

山田美奈子 2024-08-06 05:44:36

有一个 PHPUnit 扩展在内部使用 runkit,并且能够使用调用匹配器、参数约束以及模拟对象可以执行的所有操作。

https://github.com/tcz/phpunit-mockfunction

There is a PHPUnit extension that uses runkit internally, and is capable to use invocation matchers, parameter constraints and everything a mocked object can do.

https://github.com/tcz/phpunit-mockfunction

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文