Издевательство над красноречивой моделью

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

Я использую Laravel 4.2, PHPUnit 4.3.1 и последнюю версию Mockery от Composer. Я запускаю тест PHPUnit на моем контроллере, AppController, и я хотел бы перехватить сохранение базы данных Eloquent(), издеваясь над моделью. Хотя при создании макета ошибок не возникает, макет также не прикрепляется к моей модели, поэтому строки все еще создаются.

Что мне здесь не хватает? Спасибо!

Моя ошибка из Насмешки: Mockery\Exception\InvalidCountException: Method save() from Mockery_0_EventRsvp should be called exactly 1 times but called 0 times.

// Located at /app/tests/app/AppControllerTest.php
class AppControllerTest extends TestCase {
    public function setUp() {
        parent::setUp();
        Session::start();
        Mail::pretend();
    }
    public function tearDown() {
        parent::tearDown();
        \Mockery::close();
    } 
    public function testPostApp() {
        $myvar = array();
        $this->mock = \Mockery::mock('Eloquent','EventRsvp');
        $this->app->instance('EventRsvp', $this->mock);
        $this->mock
                ->shouldReceive('save')
                ->once()
                ->andReturn('true');
        $response = $this->call('POST', '/3tDYSL0', $myvar);
    }
}

// Located at /app/controllers/AppController
class AppController extends BaseController {
    public function saveApp($shortUrl){
        $rsvp = new EventRsvp;
        $rsvp->fieldone = '124';
        $rsvp->fieldtwo = '30233';
        $rsvp->save();

        $returnredirect = Redirect::to(Request::path(). '/complete');
        return $returnredirect;
    }
}

// Located at /app/models/EventRsvp.php
<?
class EventRsvp extends Eloquent {

    protected $guarded = array('id');
    use Illuminate\Database\Eloquent\SoftDeletingTrait;
    protected $dates = ['deleted_at'];

    public function relationshipone()
    {
        return $this->belongsTo('RelationshipOne','idone');
    }

    public function relationshiptwo()
    {
        return $this->belongsTo('RelationshipTwo','idtwo');
    }

}
?>

person Luke Shaheen    schedule 10.10.2014    source источник
comment
Ваш код является примером не тестируемого. Прочтите о внедрении зависимостей и о том, почему «плохо» использовать new Class в вашем контроллере. Вы не можете ссылаться на этот new Class, издеваясь, вам нужно внедрить макет в свой контроллер.   -  person Jarek Tkaczyk    schedule 10.10.2014
comment
@JarekTkaczyk Хм, после прочтения, да, ты прав. Вся моя кодовая база написана таким образом, похоже, передо мной стоит большая задача. Можете ли вы сделать свой комментарий ответом? Выглядит достаточно хорошо, чтобы отметить как ответ мне.   -  person Luke Shaheen    schedule 10.10.2014


Ответы (1)


Используйте внедрение зависимостей вместо вызова new EventRsvp.

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

Просто чтобы вы знали, хотя добиться того, что вы пытались, возможно, я бы не стал этого делать:

$mock = Mockery::mock('overload:EventRsvp');
$mock->shouldReceive('save')->once()->andReturn(true);

$response = $this->call('POST', '/3tDYSL0', $myvar);

Это моделирование экземпляра с помощью Mockery, и он перехватит вызов new EventRsvp в вашем контроллере и будет использовать этот $mock вместо реальной модели. Но опять же, используйте внедрение зависимостей, это то, что вам нужно.

person Jarek Tkaczyk    schedule 10.10.2014
comment
Спасибо за предоставленный обходной путь - потребуется некоторое время, чтобы преобразовать всю мою кодовую базу для использования DI, поэтому мне придется использовать обходной путь, пока я это делаю. Однако, когда я использую overload, как вы показали, я получаю эту ошибку: PHP Fatal error: Call to a member function mockery_getExpectations() on a non-object. Кажется, ни у кого в Google нет такой проблемы... есть идеи? - person Luke Shaheen; 13.10.2014
comment
Хорошо - добавил путь, так что теперь это Mockery::mock('overload:app\models\EventRsvp');, и он работает без ошибок mockery_getExpectations() или моих исходных ошибок! Однако строка EventRsvp все еще создается. Как я могу предотвратить это? - person Luke Shaheen; 13.10.2014
comment
Похоже, добавление overload: не учитывает вызов once(), и поэтому исходного исключения больше нет. Если я изменю макет на что-то несуществующее, например Mockery::mock('overload:adsfadsf');, тест все равно будет успешно выполняться без ошибок, даже если этот метод никогда не вызывается в контроллере. - person Luke Shaheen; 13.10.2014
comment
Нет, он уважает once, как обычно. Однако, если вы случайно введете EventRsvp или создадите его где-то еще, это может вызвать проблемы. Поэтому убедитесь, что вы не вставляете его в конструктор, или лучше используйте DI сейчас - person Jarek Tkaczyk; 13.10.2014
comment
Аааа, все, я его в конструкторе вводил - смесь всех моих тестов. Теперь работает корректно. Спасибо, что показали правильный путь, DI и временный обходной путь! - person Luke Shaheen; 13.10.2014