Pee*_*rBr 5 php phpunit unit-testing
我很乐意写单元测试,但是当我将它们一起运行时它们会发生冲突.我正在测试这个课程:
class MyClass {
public function sayHello() {
return 'Hello world';
}
}
Run Code Online (Sandbox Code Playgroud)
使用这个测试.所有测试都有这样的结构:
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public static function setUpBeforeClass() {
require_once('path/to/MyClass.php');
}
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
Run Code Online (Sandbox Code Playgroud)
MyClassTest自己运行得很好.但PHPUnit会崩溃,因为我重新启动了类,如果另一个测试模拟MyClass并且碰巧先运行:
class Inject {
private $dependency;
public function __construct(MyClass $myClass) {
$this->dependency = $myClass;
}
public function printGreetings() {
return $this->dependency->sayHello();
}
}
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals('Hello world', $subject->printGreetings());
}
}
Run Code Online (Sandbox Code Playgroud)
我确实使用bootstrap.php伪造一些尚未重构的全局函数.
我没有自动加载器,也不想处理 - 隔离每个测试,因为它需要永远.我尝试在测试1和2的docblock中插入组合@runTestsInSeparateProcesses和@preserveGlobalState enabled/禁用,我仍然得到相同的错误.
要了解此行为,您需要了解PHPUnit的工作原理.getMockBuilder()->getMock(),为mock类动态创建以下代码:
class Mock_MyClass_2568ab4c extends MyClass implements PHPUnit_Framework_MockObject_MockObject
{
private static $__phpunit_staticInvocationMocker;
private $__phpunit_invocationMocker;
public function __clone()
{
$this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker();
}
public function sayHello()
{
$arguments = array();
$count = func_num_args();
if ($count > 0) {
$_arguments = func_get_ ...
# more lines follow ...
Run Code Online (Sandbox Code Playgroud)
如果MyClass此时尚未加载,则添加以下虚拟声明:
class MyClass
{
}
Run Code Online (Sandbox Code Playgroud)
然后使用eval()(!)解析此代码.
由于PHPUnit InjectTest 之前 将执行MyClassTest,这意味着模拟构建器将定义虚拟类,并且MyClass在MyClassTest::setUpBeforeClass被调用时已经定义.这就是错误的原因.我希望我能解释一下.否则,深入了解PHPUnit的代码.
解:
删除setUpBeforeClass()方法并将require_once语句放在测试之上.setUpBeforeClass()不适合包括课程.请参阅文档.
顺便说一句,拥有require_once顶部将工作,因为PHPUnit将包括每个测试文件在开始第一次测试之前.
测试/ MyClassTest.php
require_once __DIR__ . '/../src/MyClass.php';
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
Run Code Online (Sandbox Code Playgroud)
测试/ InjectTest.php
require_once __DIR__ . '/../src/Inject.php';
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals(TRUE, $subject->printGreetings());
}
}
Run Code Online (Sandbox Code Playgroud)