Создание фиктивного объекта со свойствами по умолчанию - PullRequest
11 голосов
/ 17 февраля 2012

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

$mock = $this->getMock('MyClass', array(), array(), 'MyClass_Mock');
$mock->prop = 'foobar';

$myclassMock = new get_class($mock);
var_dump($myclassMock->prop); // NULL
// How can I make this dump 'foobar' ?

Я тестирую часть инфраструктуры, которая определяет, определяет местонахождение и создает экземпляры этих классов, поэтому внедрение поддельного объекта отрицательно скажется на цели теста.

Мне не нужно издеваться над какими-либо методами ... просто динамически создайте смоделированный класс, например так:

class MyClass_Mock extends MyClass {
  public $prop = 'foobar';
}

Редактировать: Упрощенный пример

Ответы [ 3 ]

4 голосов
/ 17 февраля 2012

Как вы относитесь к использованию Reflection?

$r = new ReflectionClass('MyClass');

$props = $r->getDefaultProperties();

$mock = new stdClass;

foreach ($props as $prop => $value) {
    $mock->$prop = $value;
}

Я сам не много использовал Reflection, только для базового самоанализа. Я не уверен, что вы сможете полностью имитировать видимость и т. Д., Используя его, но я не понимаю, почему нет, если вы продолжите идти по пути записи в строку и eval ing.

Edit:

Сканирование с помощью функций Reflection из любопытства позволяет полностью имитировать класс с помощью фиктивных методов, реализующих ограничения полной видимости, константы и статические элементы, где это уместно, если вы динамически строите класс в строке и eval Это.

Однако, похоже, это будет завершенная миссия , чтобы действительно полностью поддерживать все возможности, когда дело доходит до получения правильных типов данных (вам потребуется код для перестройки массива конструктор из массива например)

Желаю удачи, если вы пойдете по этому пути, для этого потребуется больше умственных способностей, чем я готов сейчас сэкономить:)

Вот немного кода, вы можете сделать то же самое с константами и создать пустые методы аналогичным образом.

class Test
{
    private static $privates = 'priv';
    protected $protected = 'prot';
    public $public = 'pub';
}

$r = new ReflectionClass('Test');

$props = $r->getDefaultProperties();

$mock = 'class MockTest {';

foreach ($props as $prop => $value) {
    $rProp = $r->getProperty($prop);


    if ($rProp->isPrivate()) {
        $mock .= 'private ';
    }
    elseif ($rProp->isProtected()) {
        $mock .= 'protected ';
    }
    elseif ($rProp->isPublic()) {
        $mock .= 'public ';
    }

    if ($rProp->isStatic()) {
        $mock .= 'static ';
    }

    $mock .= "$$prop = ";

    switch (gettype($value)) {
        case "boolean":
        case "integer":
        case "double":
            $mock .= $value;
            break;
        case "string":
            $mock .= "'$value'";
            break;
/*
"array"
"object"
"resource"
*/
    case "NULL":
            $mock .= 'null';
            break;
    }

    $mock .= ';';
}

$mock .= '}';

eval($mock);

var_dump(new MockTest);
3 голосов
/ 17 февраля 2012

Я не уверен, что вам даже нужно , чтобы сделать это в целях тестирования.

Обычно при тестировании кода, включающего доступ к модели, вы используете фиксации вместо насмешки над фактическими моделями, потому что модели - это "глупые" структуры данных, которые не предоставляют никаких возможностей, которые нужно моделировать.

Ваш пример подтверждает это: если вам не нужно имитировать поведение (методы), вам не нужен фиктивный объект . Вместо этого вам понадобится фиксатор данных, который модель использует в качестве источника данных. Это особенно верно, если, как вы говорите, «внедрение зависимостей не вариант» .

Конечно, если вы решите, что хотите в любом случае смоделировать модель, я бы предложил решение @ Leigh для отражения.

Я только что ответил на вопрос о тестировании базы данных вчера, что вы можете проверить немного подробнее: PHPUnit: Как проверить взаимодействие с базой данных на удаленном сервере Postgres?

1 голос
/ 17 февраля 2012

Я думаю, что проблема в том, что у тестируемой системы (вашей инфраструктуры) должна быть возможность использовать new для непосредственного создания экземпляров объектов модели, и каждый тест должен устанавливать значения по умолчанию для своих свойств по-своему.

Если это так, вы можете создать простой базовый класс для заполнения предопределенного набора свойств при создании.В приведенном ниже решении используется поздняя статическая привязка из PHP 5.3, но вы можете легко добиться тех же результатов без него с небольшим изменением.

class MockModel
{
    public static $properties;

    public function __construct() {
        if (isset(static::$properties) && is_array(static::$properties)) {
            foreach (static::$properties as $key => $value) {
                $this->$key = $value;
            }
        }
    }
}

class MockBook extends MockModel { /* nothing needed */ }

function testBookWithTitle() {
    MockBook::$properties = array(
        'title' => 'To Kill a Mockingbird'
    );
    $book = new MockBook;
    self::assertEquals('To Kill a Mockingbird', $book->title);
}

Пока вы можете указать имя классаиспользовать с new для вашей платформы, это должно работать.Если вам нужно иметь возможность создавать более одного экземпляра одного и того же фиктивного класса во время одного вызова вашей платформы, вам нужно усовершенствовать вышеупомянутое с помощью своего рода механизма индексации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...