Laravel Virgin:在测试利用关系的模型更改器时,禁用/模拟数据库连接

时间:2019-06-24 09:52:19

标签: php laravel unit-testing phpunit

我有以下型号:

  • Grid.php
namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use App\Model\Rover;

class Grid extends Model
{
    /**
     * Table Name
     */
    protected $table='grid';

    public function rovers()
    {
        return $this->hasMany(Rover::class);
    }
}
  • Rover.php
namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use App\Model\Grid;

class Rover extends Model
{

    /**
     * Table Name
     */
    protected $table='rover';

    public function grid()
    {
        return $this->belongsTo(Grid::class);
    }

    public function setGridPosXValue($value)
    {
        $Grid = $this->grid()->first();
        $width = $Grid->width;
        if($value < 0 || $value > $width){
            throw new \InvalidArgumentException("X is out of grid bounds");
        }

        $this->attributes['x']=$value;
    }

    public function setGridPosYValue($value)
    {
        $Grid = $this->grid()->first();
        $height = $Grid->height;

        if($value < 0 || $value > $height){
            throw new \InvalidArgumentException("Y is out of grid bounds");
        }

        $this->attributes['y']=$value;
    }
}

我想用theese测试来测试Rover的mutator:

namespace Tests\Unit;

use Tests\TestCase;
use App\Model\Rover;
use App\Model\Grid;
use Mockery;

define("TEST_ROVEL_SUCCESS_VAL",3);
define("TEST_ROVEL_NEGATIVE_VAL",-1);


class RoverModelTest extends TestCase
{
    public function testSetGridPosXValueThrowsExceptionWhenNegative(){
        $this->markTestSkipped('To be implemented');
    }

    public function testSetGridPosXValueThrowsExceptionWhenOverWidth(){
        $rover = new Rover();
        $grid = new Grid();
        $grid->width=5;
        $grid->height=5;
        $rover->grid=$grid;

        $this->expectException(\InvalidArgumentException::class);
        $rover->setGridPosXValue(TEST_ROVEL_NEGATIVE_VAL);

    }

    public function testSetGridPosXValueThorwsExceptionOnNormal(){
        $rover = new Rover();
        $grid = new Grid();

        $grid->width=5;
        $grid->height=5;

        $rover->grid=$grid;
        try {
            $rover->setGridPosXValue(TEST_ROVEL_SUCCESS_VAL);
            $this->assertEquals($rover->x,TEST_ROVEL_SUCCESS_VAL);
        } catch(\Exception $e) {
            $this->fail("No exception should be thrown when setting a correct value.");
        }
    }
}

因此,当我运行测试时,出现以下错误:

PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

SFF                                                                 3 / 3 (100%)

Time: 125 ms, Memory: 18.00 MB

There were 2 failures:

1) Tests\Unit\RoverModelTest::testSetGridPosXValueThrowsExceptionWhenOverWidth
Failed asserting that exception of type "Illuminate\Database\QueryException" matches expected exception "InvalidArgumentException". Message was: "SQLSTATE[08006] [7] FATAL:  password authentication failed for user "laravel" (SQL: select * from "grid" where "grid"."id" is null limit 1)" at
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:46
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/PostgresConnector.php:33
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/ConnectionFactory.php:182
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:918
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:943
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:399
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:325
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:657
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:624
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:333
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2124
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2112
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2598
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2113
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:521
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:505
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Concerns/BuildsQueries.php:77
/var/www/html/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:23
/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php:386
/var/www/html/app/Model/Rover.php:23
/var/www/html/tests/Unit/RoverModelTest.php:28
.

2) Tests\Unit\RoverModelTest::testSetGridPosXValueThorwsExceptionOnNormal
No exception should be thrown when setting a correct value.

/var/www/html/tests/Unit/RoverModelTest.php:44

FAILURES!
Tests: 3, Assertions: 2, Failures: 2, Skipped: 1.

这意味着测试命中了错误的数据库连接。

那么,如何在测试时设置“伪”数据库连接以完全不使用任何数据库?之所以这样,是因为我想做单元测试而不是集成测试,而在集成之前我还要测试dastabase连接。

在我的情况下,我希望代码不要打到db连接并测试mutator所具有的逻辑,而无需完全写入数据库。

1 个答案:

答案 0 :(得分:1)

您使用的是$this->grid()->first()而不是$this->grid,这会强制使用数据库连接来获取网格。

您可以使用部分模拟来实现所需的功能:

class RoverModelTest extends TestCase
{
    private $rover;

    // Avoid code duplication
    protected function setUp() : void { 
        parent::setUp();
        $this->rover = $this->getMockBuilder(Rover::class)
                 ->setMethods('grid')
                 ->getMock();        
        $grid = new Grid();
        $grid->width=5;
        $grid->height=5;
        $this->rover->expects($this->any())->method('grid')->willReturn(collect([$grid]));

    }

    public function testSetGridPosXValueThrowsExceptionWhenNegative(){
        $this->markTestSkipped('To be implemented');
    }

    public function testSetGridPosXValueThrowsExceptionWhenOverWidth(){
        $this->expectException(\InvalidArgumentException::class);
        $this->rover->setGridPosXValue(TEST_ROVEL_NEGATIVE_VAL);
    }

    public function testSetGridPosXValueThorwsExceptionOnNormal(){       
        try {
            $this->rover->setGridPosXValue(TEST_ROVEL_SUCCESS_VAL);
            $this->assertEquals($this->rover->x,TEST_ROVEL_SUCCESS_VAL);
        } catch(\Exception $e) {
            $this->fail("No exception should be thrown when setting a correct value.");
        }
    }
}