phpunit уникати аргументів конструктора для mock


85

Як уникнути необхідності phpunit викликати конструктор для макетного об’єкта? В іншому випадку мені знадобився б фіктивний об'єкт як аргумент конструктора, ще один для цього і т. Д. API, схоже, такий:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName = '', $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

Я не змушую це працювати. Він все ще скаржиться на аргумент конструктора, навіть якщо $callOriginalConstructorвстановлено значення false.

У мене є лише один об’єкт у конструкторі, і це введення залежностей. Тому я не думаю, що у мене там проблема з дизайном.

Відповіді:


139

Ви можете використовувати getMockBuilderзамість просто getMock:

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

Дивіться розділ «Test двійників» в документації PHPUnit в протягом деталей.

Хоча ви можете це зробити, набагато краще не потрібно. Ви можете рефакторингувати свій код, щоб замість конкретного класу (з конструктором), який потрібно вводити, ви залежали лише від інтерфейсу. Це означає, що ви можете знущатися над інтерфейсом, не вимагаючи PHPUnit змінювати поведінку конструктора.


Для мене це чудово працює. Це має бути приклад 10.3. Я спробував відредагувати публікацію, але ТО відкинув її за занадто коротку редакцію.
Метью

Дякую @Lytithwyn. Відповідь оновлено.
dave1010

42

Ось:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName = 'MyMockSoapClient';
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);

Здається, це майже те, що я хочу. Я хочу зателефонувати у getMock із лише класом, над яким знущаються, та $ callMockConstructor. Як? приблизно так: $ this-> getMock ($ classToMock, $ callMockConstructor). Єдине, про що я міг подумати, це зайти у джерело PHPUnit і змінити його на default = false.
Gutzofter

1
Я змінив за замовчуванням значення false у testcase.php. Можна подумати, що за замовчуванням для нього буде встановлено значення false. Знущання над конструктором здаються дуже дивними
Gutzofter

Відмінна відповідь. Саме те, що я шукав
Аїд

4

Як додаток, я хотів приєднати expects()виклики до мого знущаного об'єкта, а потім викликати конструктор. У PHPUnit 3.7.14 об’єкт, який повертається під час виклику, disableOriginalConstructor()є буквально об’єктом.

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

На жаль, у PHP 5.4 є нова опція, яку вони не використовують:

ReflectionClass :: newInstanceWithoutConstructor

Оскільки це було недоступно, мені довелося вручну відображати клас, а потім викликати конструктор.

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

Зверніть увагу, якщо functionCallFromConstructє protected, то ви напевне повинні використовувати setMethods()таким чином , що захищений метод знущався. Приклад:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods()необхідно expect()дзвонити перед дзвінком. Особисто я прив'язую це після, disableOriginalConstructor()але раніше getMock().


Поняття не маю, чи це запах коду, але це мені вдало, і я просто хотів подякувати.
devbanana

1

Можливо, вам потрібно створити заглушку для передачі в якості аргументу конструктора. Тоді ви можете розірвати цей ланцюжок макетних предметів.


1

Крім того, ви можете додати параметр до getMock, щоб запобігти виклику конструктора за замовчуванням.

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName = '', callOriginalConstructor = FALSE);

Тим не менше, я думаю, що відповідь dave1010 виглядає приємніше, це лише заради повноти.


1

Це питання трохи старе, але для нових відвідувачів ви можете зробити це за допомогою createMockметоду (раніше викликаного createTestDoubleта введеного у v5.4.0).

$mock = $this->createMock($className);

Як ви можете бачити в коді, витягнутому з PHPUnit\Framework\TestCaseкласу (in phpunit/src/framework/TestCase.php), він в основному створить макетний об'єкт, не викликаючи оригінальний конструктор .

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}

0

PHPUnit призначений для виклику конструктора на знущані об'єкти; щоб запобігти цьому, вам слід:

  1. Вставте макетний об'єкт як залежність від об'єкта, з яким ви маєте проблеми з глузуванням
  2. Створіть тестовий клас, який розширює клас, який ви намагаєтесь викликати, який не викликає батьківський конструктор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.