3
votes

I've looked through literally hundreds of pages, and I can't seem to find what makes my situation different than the working examples I've found on these pages.

I'm using Laravel 4.2, PHPUnit 4.3.1, and latest Mockery from Composer. I'm running a PHPUnit test on my Controller, AppController, and I'd like to intercept an Eloquent database save() by mocking the model. While there are no errors thrown when I create the Mock, the mock also isn't attaching to my model, so the rows are still being created.

What am I missing here? Thanks!

My error from Mockery: 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');
    }

}
?>
1
Your code is example of non testable one. Read about dependency injection, and why it is 'bad' to use new Class in your controller. You can't refer to that new Class by mocking, you need to inject mock to your controller.Jarek Tkaczyk
@JarekTkaczyk Hmm, after reading, yep, you're right. My entire codebase is written this way, looks like I have a big task in front of me. Can you make your comment a answer? Looks good enough to mark as the answer to me.Luke Shaheen

1 Answers

3
votes

Use dependency injection instead of calling new EventRsvp.

You may want to create a repository that will provide EventRsvps to your controller, then you can easily mock the repo and inject it instead of the real one.

Just so you know, while it is feasible to achieve what you tried, I wouldn't do that:

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

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

This is instance mocking with Mockery, and it will catch new EventRsvp call in your controller, and use that $mock instead of real model. But again, use dependency injection, it is what you want.