简体   繁体   中英

Mocking Laravel 5.2 local scope with phpunit and Mockery

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)?

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));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM