我有一个类似于此的类(为简洁起见,删除了一些逻辑):
class FooCollection {
protected $_foos;
public function __construct() {
$this->_foos = new SplObjectStorage();
}
public function addFoo(FooInterface $foo) {
$this->_foos->attach($foo);
}
public function removeFoo(FooInterface $foo) {
$this->_foos->detach($foo);
}
}
我想使用PHPUnit测试addFoo()
和removeFoo()
方法,我想知道最好的策略是什么?据我所知,我只有几个选择:
hasFoo(FooInterface $foo)
并在添加后检查此内容。getFoos()
实例的方法SplObjectStorage
,并在添加后检查$foo
是否在其中。removeFoo($foo)
之后尝试addFoo($foo)
并检查例外情况。$_foos
设为公共财产,并在添加后直接检查(坏,坏,坏......)。选项#1和#2正在更改公共接口仅用于测试目的,我不确定我的感受。它们似乎是非常通用的,有用的方法,但在我的特定情况下,我从来不需要检查集合中特定Foo
实例的存在,也不需要检索所有实例,所以它真的会膨胀。此外,似乎如果我在一次测试中测试界面的多个部分,我不是真的在测试“单位”,但这或多或少只是一个哲学上的挂断。
选项#3对我来说似乎很尴尬。
选项#4是一个非常糟糕的主意,我甚至不应该列出它,因为即使在这里建议也不会这样做。
答案 0 :(得分:5)
为什么不创建一个传递给构造函数的模拟SplObjectStorage
对象?然后,您可以声明在模拟上调用attach
和detach
方法。
function testAttachFOO() {
$mockStorage = $this->getMockBuilder('SplObjectStorage')
->setMethods(array('attach'))
->getMock();
$mockFoo = $this->getMock('FooInterface');
$mockStorage->expects($this->once())
->method('attach')
->with($mockFoo);
$collection = new FooCollection($mockStorage);
$collection->addFoo($mockFoo);
}
removeFoo
的类似内容。
执行此操作需要您更改构造函数,以便可以注入依赖项。但IMO使得代码更加清晰。也使测试更容易。
所以构造函数变为:
public function __construct(SPLObjectStorage $storage) {
$this->_foos = $storage;
}
如果这个类很难构建,那就表明该类做得太多了,应该重构为更多更小的类。
答案 1 :(得分:0)
您没有发布用于获取集合的公共访问器,但我确信您有一个,否则添加/删除foos到一个公共无法访问的数组是没有意义的。所以你可以试试(phpunit 3.6,php 5.4):
public function setUp()
{
$this->NumbersCollection = new NumbersCollection;
}
public function tearDown()
{
unset($this->NumbersCollection);
}
public function testNumbersCollection()
{
$this->NumbersCollection->addNumber(1);
$this->NumbersCollection->addNumber(2);
$this->assertSame(3, $this->NumbersCollection->sum());
$this->assertSame(2, $this->NumbersCollection->product());
$this->NumbersCollection->removeNumber(1);
$this->NumbersCollection->addNumber(7);
$this->assertSame(9, $this->NumbersCollection->sum());
$this->assertSame(14, $this->NumbersCollection->product());
}