3

I'm using Laravel 5.2, phpunit 5.0.0 and PHP 7.0.3 and try to write a test with database interaction that touches an Eloquent models scope method.

I have a something like that:

class Picture extends Illuminate\Database\Eloquent\Model {
    ...
    public function scopeGetPictureNameById($oQuery, $pictureHId) {
         return $oQuery->select('name')->where('h_id', '=',   $pictureHId)->first()->name;
    }
}

class someHelperClass {
    public function someMethod($pictureId) {
        $pictureName = Picture::getPictureNameById($pictureId);
        return "name is " . $pictureName;
    }
}


class SomeTest extends TestCase {

    use DatabaseMigrations;

    protected $someHelper;

    public function setUp() {
        parent::setUp();
        $this->someHelper = new SomeHelper();
    }

    /**
     * @test
     */
    public function someMethodTest() {
        $expectedName = "test";
        $this->assertEquals("name is " . $expectedName, $this->someHelper->someMethod());
    }
}

I seed the Database with a Picture record where the name is set to "test".

The first thing I thought was that I would not have to mock the scope call, because all I need is in the Database. And since the (non simplified) code I have works outside the test, I guess that scope calls don't work in phpunit. (I get a "Trying to get property of non-object" Exception).

Okay, so I tried to mock the call with Mockery:

class SomeTest extends TestCase {

    use DatabaseMigrations;

    protected $someHelper;

    public function setUp() {
        parent::setUp();
        $this->someHelper = new SomeHelper();
    }

    /**
     * @test
     */
    public function someMethodTest() {
        $expectedName = "test";

        $mockedPicture = Mockery::mock('overload:App\Models\Picture');
        $mockedPicture->shouldReceive('getPictureNameById')->andReturn('test');

        //also tried this: $mockedPicture->shouldReceive('scopeGetPictureNameById')->andReturn('test');


        $this->assertEquals("name is " . $expectedName, $this->someHelper->someMethod());
    }
}

All I get is the "Could not load mock App\Models\Picture, class already exists". So how can I properly mock query scope calls like Picture::getPictureNameById($pictureId)?

1 Answer 1

1

I would use dependency injection instead of calling methods on the Picture class statically. So something like this:

class someHelperClass {

    protected $picture;

    public function __construct(Picture $picture) {
        $this->picture = $picture;
    }

    public function someMethod($pictureId) {
        $pictureName = $this->picture->getPictureNameById($pictureId);
        return "name is " . $pictureName;
    }
}

Then in your test:

public function someMethodTest() {
    $expectedName = "test";

    $mockedPicture = Mockery::mock('App\Models\Picture');
    $mockedPicture->shouldReceive('getPictureNameById')->andReturn('test');
    $someHelper = new SomeHelper($mockedPicture);

    $this->assertEquals("name is " . $expectedName, $someHelper->someMethod(1));
}
Sign up to request clarification or add additional context in comments.

2 Comments

I'm using Laravel query scopes (laravel.com/docs/5.1/eloquent#query-scopes). How can I call them non-statically?
You can do this if you use dependency injection as in my example. Maybe this post clarifies it. Did it not work for you?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.