Единичный тест для издевательства метода, вызванного новым объектом класса

Я пишу модульный тест для существующего кода, который подобен этому

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);