单元测试:如何独立于父类测试子类?
我开始使用 PHPUnit 对我的 PHP 应用程序进行单元测试。我知道单元测试独立运行非常重要,这样您就知道在测试失败时应该去哪里查找。我很难理解的一件事是如何与父类隔离地测试子类。例如,我的大多数模型都扩展了“基本模型”,它具有模型应具有的大部分功能。
<?php
class BaseModel
{
public function save($data) {
// write $data to the database
$dbAdapter->save($data);
}
}
class RegularModel extends BaseModel
{
public function save($data) {
// clean up $data before passing it to parent
if (isset($data['foo'])) {
unset($data['foo']);
$data['bar'] = 'foo';
}
parent::save($data);
}
}
# Unit Test
class RegularModelTest extends PHPUnit_Framework_TestCase
{
public function testSaveMethodCallsParent() {
$data = array('foo' => 'yes');
$model = new RegularModel();
$model->save($data);
// assert parent received data correctly
}
}
我不确定如何在不调用一堆不必要的代码的情况下测试我的 RegularModel
。我还进行了一些自动加载,因此当它在父级上调用 save 时,它实际上会尝试保存到测试数据库。我宁愿模拟这一点,因为只有当我测试我的 BaseModel
时,我才关心它是否在测试我的 RegularModel
时实际写入数据库>。还是我的想法全错了?当涉及到这样的测试情况时,您有什么建议?
I'm getting started unit testing my PHP application with PHPUnit. I understand that it's important for unit tests to run in isolation so you know where to look when a test fails. One thing I am struggling to understand is how to test subclasses in isolation from their parent. For example, most of my models extend a "base model" which has most of the features that a model should have.
<?php
class BaseModel
{
public function save($data) {
// write $data to the database
$dbAdapter->save($data);
}
}
class RegularModel extends BaseModel
{
public function save($data) {
// clean up $data before passing it to parent
if (isset($data['foo'])) {
unset($data['foo']);
$data['bar'] = 'foo';
}
parent::save($data);
}
}
# Unit Test
class RegularModelTest extends PHPUnit_Framework_TestCase
{
public function testSaveMethodCallsParent() {
$data = array('foo' => 'yes');
$model = new RegularModel();
$model->save($data);
// assert parent received data correctly
}
}
I'm not sure how to test my RegularModel
without calling a bunch of unnecessary code. I'm also doing some autoloading so when it calls save on the parent, it will actually try to save to the test database. I'd rather mock this out since I don't care about whether or not it actually writes to the database when I'm testing my RegularModel
only when I am testing my BaseModel
. Or am I thinking about this all wrong? What do you recommend when it comes to testing situations like this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最好的选择是在测试
RegularModel
时模拟$dbAdapter
。当您仍在执行父类的代码时,由于 is-a 关系,该代码实际上是RegularModel
单元的一部分。解决这个问题的唯一方法是提供 BaseModel 的不同实现。您可以在单独的进程中运行这些测试,也可以使用 Runkit 来交换运行时的其他实现。这两者都有缺点——复杂性、性能下降和不稳定——在我看来,这些缺点的代价太高了。
Your best bet is to mock the
$dbAdapter
when testingRegularModel
. While you are still executing the parent class's code, that code really is part of theRegularModel
unit due to the is-a relationship.The only way around it would be to provide a different implementation of
BaseModel
. You can either run these tests in a separate process or use Runkit to swap in the other implementation at runtime. Both of these have drawbacks--complexity, performance degredation, and instability--that come at too high a price in my view.根据定义,子类与其超类紧密耦合,因此实际上没有办法单独测试子类。
但是,如果超类具有通过的广泛测试套件,那么您通常可以确信只需覆盖子类中实现的方法即可测试子类。超类测试套件涵盖了超类功能。
Subclasses are, by definition, tightly coupled to their superclasses so practically speaking there is no way to test a subclass in isolation.
However, if the superclass has an extensive test suite that passes then you normally can be confident that you can test the subclass by just covering the methods implemented in the subclass. The superclass test suite covers the superclass functionality.