是否有相当于 SimpleTest 的“部分模拟”? 在 PHPUnit 中?

发布于 2024-07-28 23:52:51 字数 887 浏览 5 评论 0原文

我正在尝试将一堆测试从 SimpleTest 迁移到 PHPUnit 我想知道是否有相当于 SimpleTest 的 部分模拟

我似乎在文档中找不到任何表明此功能可用的内容,但我突然想到我可以只使用一个子类。 这是好主意还是坏主意?

class StuffDoer {
    protected function doesLongRunningThing() {
        sleep(10);
        return "stuff";
    }
    public function doStuff() {
        return $this->doesLongRunningThing();
    }
}

class StuffDoerTest {
    protected function doesLongRunningThing() {
        return "test stuff";
    }
}

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $sd = new StuffDoerTest();
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}

I'm trying to migrate a bunch of tests from SimpleTest to PHPUnit and I was wondering if there is an equivalent for SimpleTest's partial mocks.

I can't seem to find anything in the documentation which suggests that this feature is available, but it occurred to me that I could just use a subclass. Is this a good or bad idea?

class StuffDoer {
    protected function doesLongRunningThing() {
        sleep(10);
        return "stuff";
    }
    public function doStuff() {
        return $this->doesLongRunningThing();
    }
}

class StuffDoerTest {
    protected function doesLongRunningThing() {
        return "test stuff";
    }
}

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $sd = new StuffDoerTest();
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}

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

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

发布评论

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

评论(5

情独悲 2024-08-04 23:52:51

PHPUnit < 5.4

从阅读链接页面来看,SimpleTest 部分模拟似乎是仅覆盖部分方法的模拟。 如果这是正确的,则该功能由普通的 PHPUnit 模拟处理。

PHPUnit_Framework_TestCase 内,您创建一个模拟,

$mock = $this->getMock('Class_To_Mock');

它会创建一个模拟实例,其中所有方法都不执行任何操作并返回 null。 如果您只想重写某些方法,getMock 的第二个参数是要重写的方法数组。

$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));

将创建一个 Class_To_Mock 的模拟实例,并删除 insertupdate 函数,准备好指定它们的返回值。

此信息位于 phpunit 文档

注意此答案显示了更多最新的代码示例,适用于从 5.4 开始的 PHPUnit 版本

PHPUnit < 5.4

From reading the linked page, a SimpleTest partial mock seems to be a mock where only some of the methods are overridden. If this is correct, that functionality is handled by a normal PHPUnit mock.

Inside a PHPUnit_Framework_TestCase, you create a mock with

$mock = $this->getMock('Class_To_Mock');

Which creates an mock instance where all methods do nothing and return null. If you want to only override some of the methods, the second parameter to getMock is an array of methods to override.

$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));

will create an mock instance of Class_To_Mock with the insert and update functions removed, ready for their return values to be specified.

This information is in the phpunit docs.

Note, this answer shows more up to date code examples, for PHPUnit versions starting 5.4

雪若未夕 2024-08-04 23:52:51

PHPUnit 5.4 到 7

PHPUnit_Framework_TestCase::getMock 是自 phpunit 5.4 起已弃用。 我们可以使用setMethods来代替。

可以在 Mock Builder 对象上调用 setMethods(array $methods) 来指定要替换为可配置测试替身的方法。 其他方法的行为不会改变。 如果调用 setMethods(null),则不会替换任何方法。

https://phpunit.de/manual/current/en/test-doubles。 html

$observer = $this->getMockBuilder(Observer::class)
                 ->setMethods(['update'])
                 ->getMock();

注意,上面的getMockPHPUnit_Framework_MockObject_MockBuilder::getMock。 (phpunit5.6)

PHPUnit 5.4 to 7

PHPUnit_Framework_TestCase::getMock is deprecated since phpunit 5.4. We can use setMethods instead.

setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.

https://phpunit.de/manual/current/en/test-doubles.html

$observer = $this->getMockBuilder(Observer::class)
                 ->setMethods(['update'])
                 ->getMock();

Note that the above getMock is PHPUnit_Framework_MockObject_MockBuilder::getMock. (phpunit5.6)

巾帼英雄 2024-08-04 23:52:51

PHPUnit 8+

方法 setMethods 已弃用。 现在它可以工作了:

$mock = $this->getMockBuilder(ClassToMock::class)
    ->onlyMethods(['insert', 'update'])
    ->getMock();

PHPUnit 8+

The method setMethods was deprecated. Now it works:

$mock = $this->getMockBuilder(ClassToMock::class)
    ->onlyMethods(['insert', 'update'])
    ->getMock();
少女的英雄梦 2024-08-04 23:52:51

我认为 PHPUnit 不支持对被测系统的部分模拟。 如果您尝试隔离方法,那么我确信您的实现是有效的 - 我也这样做了。

然而,出于几个原因,我尽量避免这样做。

首先,它将您的测试与类的内部实现紧密结合。 您真的关心是否调用了名为 doesLongRunningThing 的方法,还是“LongRunningThing”完成更重要?

其次,当我遇到这个问题时,我总是想知道是否有一个班级做两个班级的工作。 提取类重构可能是合适的。 如果 doesLongRunningThing() 成为自己的类,即使使用单个方法,测试也会变得更加容易。

我相信解决方案是注入 SUT 所依赖的服务 (http://en.wikipedia.org/wiki/Dependency_injection)。 这也使得 DoesLongRunningThing 实现更易于测试。

在不跳入界面的情况下,我会这样做:

class DoesLongRunningThing {
    public function execute() {
        sleep(10);
        return "stuff";
    }
}

class StuffDoer {
    protected $doesLongRunningThing;

    public function setLongRunningThinger(DoesLongRunningThing $obj) {
        $this->doesLongRunningThing = $obj;
    }

    public function doStuff() {
        return $this->doesLongRunningThing->execute();
    }
}

现在很容易模拟:

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $dlrtMock = $this->getMock('DoesLongRunningThing');
        $dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));

        $sd = new StuffDoer();
        $sd->setLongRunningThinger($dlrtMock);
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}

I don't think PHPUnit supports partial mocks for the system under test. If you're trying to isolate methods then I'm sure your implementation works - I've done that too.

However, I try to avoid doing this, for a couple of reasons.

First, it couples your test very tightly to the internal implementation of the class. Do you really care whether a method called doesLongRunningThing was called, or is it more important that the "LongRunningThing" got done?

Second, when I run into this it always makes me wonder whether I've got one class doing the job of two. An extract class refactoring might be in order. The testing becomes much easier if doesLongRunningThing() becomes its own class, even with a single method.

I believe the solution is to inject the services your SUT depends on (http://en.wikipedia.org/wiki/Dependency_injection). This also makes the DoesLongRunningThing implementation more testable.

Without jumping into interfaces, here's what I would do:

class DoesLongRunningThing {
    public function execute() {
        sleep(10);
        return "stuff";
    }
}

class StuffDoer {
    protected $doesLongRunningThing;

    public function setLongRunningThinger(DoesLongRunningThing $obj) {
        $this->doesLongRunningThing = $obj;
    }

    public function doStuff() {
        return $this->doesLongRunningThing->execute();
    }
}

Now it's easy to mock:

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $dlrtMock = $this->getMock('DoesLongRunningThing');
        $dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));

        $sd = new StuffDoer();
        $sd->setLongRunningThinger($dlrtMock);
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}
高冷爸爸 2024-08-04 23:52:51

可能是您正在嘲笑的类不在范围内(我遇到了这个问题)。 解决后,我能够模拟一个函数并测试另一个函数的实际逻辑。

$mockController = $this->getMockBuilder('ControllerClassName')
                       ->setMethods(['functionToMock'])
                       ->getMock();

$result = $mockController->anotherFunction();
$this->assertEquals(true, $result);

It could be that the class you are mocking is not in scope (I had this issue). When resolved, I was able to mock one function and test the actual logic of another.

$mockController = $this->getMockBuilder('ControllerClassName')
                       ->setMethods(['functionToMock'])
                       ->getMock();

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