Подтвердить что ты не робот

Метод тестирования phpunit, который вызывает другие методы класса, которые требуют макета

Я пытаюсь создать довольно стандартный unit test, где я вызываю метод и утверждаю его ответ, однако метод, который я тестирую, вызывает другой метод внутри того же класса, который делает немного тяжелый подъем.

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

Я проглотил этот пример, чтобы сделать его максимально простым.

class MyClass
{

    // I want to test this method, but mock the handleValue method to always return a set value.

    public function testMethod($arg)
    {

        $value = $arg->getValue();

        $this->handleValue($value);

    }


    // This method needs to be mocked to always return a set value.

    public function handleValue($value)
    {

        // Do a bunch of stuff...
        $value += 20;

        return $value;

    }

}

Моя попытка написать тесты.

class MyClassTest extends \PHPUnit_Framework_TestCase
{


    public function testTheTestMethod()
    {

        // mock the object that is passed in as an arg
        $arg = $this->getMockBuilder('SomeEntity')->getMock();
        $arg->expects($this->any())
            ->method('getValue')
            ->will($this->returnValue(10));

        // test handle document()
        $myClass = new MyClass();

        $result = $myClass->testMethod($arg);

        // assert result is the correct
        $this->assertEquals($result, 50);

    }

}

Я пытался издеваться над объектом MyClass, но когда я это делаю и вызываю testMethod, он всегда возвращает null. Мне нужен способ издеваться над одним методом, но остальная часть объекта не повреждена.

4b9b3361

Ответ 1

Вы можете издеваться над классом, который вы тестируете, и указать метод, который вы хотите высмеять.

$mock = $this->getMockBuilder('MyClass')
    ->setMethods(array('handleValue'))
    ->getMock();

$mock->expects($this->once())
    ->method('handleValue')
    ->will($this->returnValue(23)) //Whatever value you want to return

Однако IMO это не лучшая идея для ваших тестов. Тестирование, как это, сделает рефакторинг намного сложнее. Вы указываете реализацию класса, а не поведение, которое должен иметь класс. Если handleValue выполняет много сложной работы, что затрудняет тестирование, подумайте о том, чтобы переместить логику в отдельный класс и ввести ее в свой класс. Затем вы можете создать макет этого класса и передать его в testMethod. Это даст вам дополнительное преимущество: сделать MyClass более расширяемым, если handleValue необходимо адаптировать его поведение.

http://www.oodesign.com/strategy-pattern.html

Как правило, вы не должны издеваться над тестируемой системой.

Ответ 2

Вы можете указать, какие методы высмеивать (частичный макет) с помощью setMethods():

 // Let do a `partial mock` of the object. By passing in an array of methods to `setMethods`
 // we are telling PHPUnit to only mock the methods we specify, in this case `handleValue()`.

$csc = $this->getMockBuilder('Lightmaker\CloudSearchBundle\Controller\CloudSearchController')
             ->setConstructorArgs($constructor)
             ->setMethods(array('handleValue'))
             ->getMock();

 // Tell the `handleValue` method to return 'bla'
 $csc->expects($this->any())
     ->method('handleValue')
     ->with('bla');

Любые другие методы в классе, не указанном в массиве, который вы даете setMethods(), будут выполняться как есть. Если вы не используете, используйте setMethods, все методы вернут NULL, если вы специально их не установили.