Skip to content

Commit

Permalink
New Feature - Asserting Scope Functionality (#1)
Browse files Browse the repository at this point in the history
* add dev dependency on PHPUnit to allow hinting on Framework types

* add scope assertions

* sepearate the query mock so it can be used for advanced assertions

* fix documentation
  • Loading branch information
rossriley authored and jarektkaczyk committed Feb 12, 2019
1 parent 5a25f91 commit df85211
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 1 deletion.
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

Add `EloquentTestsuite` trait to your PHPUnit Test:


### Testing Relations

```php
class SomeModelTest extends \PHPUnit\Framework\TestCase
{
Expand All @@ -35,6 +38,70 @@ class SomeModelTest extends \PHPUnit\Framework\TestCase
}
```

### Testing Scope Definitions

Testing scope definitions provide a shorthand assertion to verify that a given scope
exists on the model, and it performs the expected filtering on the model results.

The available assertion methods are:

```php
assertScopeFilters(Model $model, string $scope, string $column, string $value)
assertScopeFiltersNull(Model $model, string $scope, string $column)
assertScopeFiltersNotNull(Model $model, string $scope, string $column)
assertScopeFiltersIn(Model $model, string $scope, string $column, array $values)
assertScopeFiltersNotIn(Model $model, string $scope, string $column, array $values)
```

```php
class SomeModelTest extends \PHPUnit\Framework\TestCase
{
use EloquentSuite;

public function testScopePublished()
{
$article = new Article();
$this->assertScopeFilters($article, 'published', 'status', 1);
}

public function testScopeAvailable()
{
$article = new Article();
$this->assertScopeFiltersNull($article, 'available', 'deleted_at');
}

public function testScopeDeleted()
{
$article = new Article();
$this->assertScopeFiltersNotNull($article, 'deleted', 'deleted_at');
}
}
```

For more advanced assertions you can just use `EloquentSuite` to create the query
and then do custom assertions on the method and parameters. For instance take
this scope definition that does a raw where query.

```php
public function scopeValidOnDate($query, $date)
{
return $query->whereRaw('? between valid_from and valid_to', [$date]);
}
```

You can unit test this definition using the following syntax:

```php
public function testScopeValidOnDate()
{
$assertion = $this->stringContains('valid_from');
$params = ['2020-10-10'];
$query = $this->createQueryMock('whereRaw', $assertion, $params);
$ticket = new Ticket();
$ticket->scopeValidOnDate($query, '2020-10-10');
}
```


## Installation

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
},
"require-dev": {
"illuminate/database": "^5.4",
"mockery/mockery" : "0.9.*|^1.0"
"mockery/mockery" : "0.9.*|^1.0",
"phpunit/phpunit" : "^7.0|^8.0"
},
"autoload": {
"psr-4": {
Expand Down
106 changes: 106 additions & 0 deletions src/EloquentSuite.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

namespace Sofa\EloquentTestsuite;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;
use Mockery;
use Illuminate\Database\Eloquent\Relations;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReflectionException;
use RuntimeException;

/**
* Trait EloquentSuite
* @mixin TestCase
*/
trait EloquentSuite
{
protected static $eloquent_relations = [
Expand Down Expand Up @@ -144,4 +153,101 @@ public static function assertRelation(string $relation, Relations\Relation $actu
' missing return statement.'
);
}

/**
* @param Model $model
* @param string $scope
* @param string $column
* @param string $value
* @throws RuntimeException
*/
public static function assertScopeFilters(Model $model, string $scope, string $column, string $value): void
{
self::runScopeAssertion($model, $scope, 'where', $column, $value);
}

/**
* @param Model $model
* @param string $scope
* @param string $column
* @throws RuntimeException
*/
public static function assertScopeFiltersNull(Model $model, string $scope, string $column): void
{
self::runScopeAssertion($model, $scope, 'whereNull', $column, null);
}

/**
* @param Model $model
* @param string $scope
* @param string $column
* @throws RuntimeException
*/
public static function assertScopeFiltersNotNull(Model $model, string $scope, string $column): void
{
self::runScopeAssertion($model, $scope, 'whereNotNull', $column, null);
}

/**
* @param Model $model
* @param string $scope
* @param string $column
* @param array $values
* @throws RuntimeException
*/
public static function assertScopeFiltersIn(Model $model, string $scope, string $column, array $values): void
{
self::runScopeAssertion($model, $scope, 'whereIn', $column, $values);
}

/**
* @param Model $model
* @param string $scope
* @param string $column
* @param array $values
* @throws RuntimeException
*/
public static function assertScopeFiltersNotIn(Model $model, string $scope, string $column, array $values): void
{
self::runScopeAssertion($model, $scope, 'whereNotIn', $column, $values);
}

/**
* @param Model $model
* @param string $scope
* @param string $filterMethod
* @param string $column
* @param $values
* @throws RuntimeException
*/
public static function runScopeAssertion(Model $model, string $scope, string $filterMethod, string $column, $values): void
{
if (!is_a(static::class, TestCase::class, true)) {
throw new RuntimeException('Calling class must be an instance of ' . TestCase::class);
}
$parameters = array_filter([$column, $values]);
$test = new static();
$query = $test->createQueryMock($filterMethod, ...$parameters);
$scopeMethod = 'scope' . ucfirst($scope);
$model->$scopeMethod($query);
}

/**
* @param string $filterMethod
* @param mixed ...$parameters
* @return Builder
* @throws \PHPUnit\Framework\Exception
* @throws \PHPUnit\Framework\MockObject\RuntimeException
* @throws ReflectionException
*/
public function createQueryMock(string $filterMethod, ...$parameters): Builder
{
$query = $this->createMock(Builder::class);
$query->expects(self::once())
->method($filterMethod)
->with(...$parameters)
->willReturnSelf();

return $query;
}
}

0 comments on commit df85211

Please sign in to comment.