Я пишу модульный тест для существующего кода, который подобен этому
class someClass { public function __construct() { ... } public function someFoo($var) { ... $var = "something"; ... $model = new someClass(); model->someOtherFoo($var); } public someOtherFoo($var){ // some code which has to be mocked } }
Здесь, как мне быть в состоянии издеваться над вызовом функции « someOtherFoo
», чтобы он не выполнял « some code
» внутри someOtherFoo
?
class someClassTest { public function someFoo() { $fixture = $this->getMock('someClass ', array('someOtherFoo')); $var = "something"; .... // How to mock the call to someOtherFoo() here } }
Можно ли издеваться над конструктором, чтобы он возвращал мою собственную сконструированную функцию или переменную?
благодаря
Везде, где у вас есть new XXX(...)
в тестируемом методе, вы обречены. Извлеките экземпляр нового метода – createSomeClass(...)
– того же класса. Это позволяет вам создать частичный макет тестируемого класса, который возвращает значение stubbed или mock из нового метода.
class someClass { public function someFoo($var) { $model = $this->createSomeClass(); // call method instead of using new model->someOtherFoo($var); } public function createSomeClass() { // now you can mock this method in the test return new someClass(); } public function someOtherFoo($var){ // some code which has to be mocked } }
В тесте mock createSomeClass()
в экземпляре, на котором вы вызываете someFoo()
, и mock someOtherFoo()
в экземпляре, который вы возвращаете из первого издевающегося вызова.
function testSomeFoo() { // mock someOtherFoo() to ensure it gets the correct value for $arg $created = $this->getMock('someClass', array('someOtherFoo')); $created->expects($this->once()) ->method('someOtherFoo') ->with('foo'); // mock createSomeClass() to return the mock above $creator = $this->getMock('someClass', array('createSomeClass')); $creator->expects($this->once()) ->method('createSomeClass') ->will($this->returnValue($created)); // call someFoo() with the correct $arg $creator->someFoo('foo'); }
Имейте в виду, что, поскольку экземпляр создает другой экземпляр того же класса, обычно задействуются два экземпляра. Вы можете использовать один и тот же макетный экземпляр здесь, если он станет более понятным.
function testSomeFoo() { $fixture = $this->getMock('someClass', array('createSomeClass', 'someOtherFoo')); // mock createSomeClass() to return the mock $fixture->expects($this->once()) ->method('createSomeClass') ->will($this->returnValue($fixture)); // mock someOtherFoo() to ensure it gets the correct value for $arg $fixture->expects($this->once()) ->method('someOtherFoo') ->with('foo'); // call someFoo() with the correct $arg $fixture->someFoo('foo'); }
Я нашел свой путь здесь, пытаясь выполнить white-box тест класса __constructor, чтобы убедиться, что он вызывает метод класса сам по себе, а некоторые данные передаются в __constructor.
В случае, если кто-то другой по той же причине, я думал, что поделился бы методом, в котором я закончил использование (без используемого в этом вопросе метода createSomeClass () в заводском стиле).
<?php class someClass { public function __constructor($param1) { // here is the method in the constructor we want to call $this->someOtherFoo($param1); } public function someOtherFoo($var){ } }
Теперь тест PHPUnit:
<?php $paramData = 'someData'; // set up the mock class here $model = $this->getMock('someClass', array('someOtherFoo'), // override the method we want to check array($paramData) // we need to pass in a parameter to the __constructor ); // test that someOtherFoo() is called once, with out test data $model->expects($this->once()) ->with($paramData) ->method('someOtherFoo'); // directly call the constructor, instead of doing "new someClass" like normal $model->__construct($paramData);