Skip to content

Commit

Permalink
add support for class based factorise
Browse files Browse the repository at this point in the history
  • Loading branch information
morrislaptop committed Jan 25, 2021
1 parent 1d7527a commit 4dd8b6e
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 18 deletions.
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,56 @@ PopoFactory::new(PersonData::class)

```

## Creating Class Based Factories

It's useful to define specific factories for particular objects, which can easily be done by extending the `PopoFactory` class.

My specifying a typehint for the `make()` method you will also get typehints in your IDE for your mocked object.

```php
<?php

/**
* @method PersonData make
*/
class PersonDataFactory extends PopoFactory
{
public static function factory(): static
{
return static::new(PersonData::class)->state([
'firstName' => 'Craig'
]);
}

public function gotNoJob() {
return $this->state([
'companyName' => null,
]);
}

public function worksAtHome() {
return $this->state(function ($attributes) {
return [
'workAddress' => $attributes['homeAddress']
];
});
}
}
```

Then using it in tests like so:

```php
$person = PersonDataFactory::factory()
->gotNoJob()
->worksAtHome()
->make();
```


## Extending

It used to be that you had to extend the factory class to utilize custom types. You can now do so through the static `registerProvider()` method on the `PropertyFactory` class. This method takes two arguments. The first should be the FQDN of the class you are providing (e.g. `Carbon\Carbon`) OR the built-in type (e.g. `string`). The second should be a callback that returns the generated value. This callback is passed two properties when called to assist in generating the value. The first is an instance of `Anteris\FakerMap\FakerMap` which can be used to help generate fake data. The second is an instance of `ReflectionProperty` which contains information about the property being generated.
You can easily extend the factory to support other data types. You can do tthis through the static `registerProvider()` method on the `PropertyFactory` class. This method takes two arguments. The first should be the FQDN of the class you are providing (e.g. `Carbon\Carbon`) OR the built-in type (e.g. `string`). The second should be a callback that returns the generated value. This callback is passed two properties when called to assist in generating the value. The first is an instance of `Anteris\FakerMap\FakerMap` which can be used to help generate fake data. The second is an instance of `ReflectionProperty` which contains information about the property being generated.

For example, to support Carbon:

Expand Down
23 changes: 7 additions & 16 deletions src/PopoFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Morrislaptop\PopoFactory;

use Morrislaptop\PopoFactory\Exceptions\InvalidObjectException;
use ReflectionClass;
use ReflectionProperty;
use Symfony\Component\Serializer\Serializer;
Expand Down Expand Up @@ -58,6 +57,7 @@ public function __construct(string $dataTransferObjectClass, Serializer $seriali
public function count(int $count): static
{
$clone = clone $this;

$clone->count = $count;

return $clone;
Expand Down Expand Up @@ -107,7 +107,7 @@ public function state($state): static
* DTO Creator
**************************************************************************/

public function make(array $attributes = []): array|object
public function make(array $attributes = []): array | object
{
// Pass attributes along as state
if (! empty($attributes)) {
Expand All @@ -131,30 +131,21 @@ protected function makeDTO(): object
$parameters = [];
$properties = $class->getProperties(ReflectionProperty::IS_PUBLIC);

// Resolve all our state options...
$preset = [];

foreach ($this->states as $state) {
$result = $state();
$preset = array_merge($preset, is_array($result) ? $result : []);
}

foreach ($properties as $property) {
if ($property->isStatic()) {
continue;
}

$propertyName = $property->getName();

if (isset($preset[$propertyName])) {
$parameters[$propertyName] = $preset[$propertyName];

continue;
}

$parameters[$propertyName] = PropertyFactory::new()->make($property);
}

foreach ($this->states as $state) {
$result = $state($parameters);
$parameters = array_merge($parameters, is_array($result) ? $result : []);
}

return $this->serializer->denormalize($parameters, $this->dataTransferObjectClass);
}

Expand Down
14 changes: 14 additions & 0 deletions tests/PopoFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Morrislaptop\PopoFactory\Tests\Popos\FamilyData;
use Morrislaptop\PopoFactory\Tests\Popos\PersonData;
use Morrislaptop\PopoFactory\Tests\Popos\PersonDataDocBlock;
use Morrislaptop\PopoFactory\Tests\Popos\PersonDataFactory;

class PopoFactoryTest extends AbstractTestCase
{
Expand Down Expand Up @@ -56,4 +57,17 @@ public function test_it_can_generate_dto_with_an_array_property()
$this->assertInstanceOf(PersonData::class, $child);
}
}

public function test_it_can_use_a_factory_to_make_single_dto()
{
$dto = PersonDataFactory::factory()
->gotNoJob()
->worksAtHome()
->make();

$this->assertInstanceOf(PersonData::class, $dto);
$this->assertEquals('Craig', $dto->firstName);
$this->assertEquals(null, $dto->companyName);
$this->assertEquals($dto->homeAddress, $dto->workAddress);
}
}
5 changes: 4 additions & 1 deletion tests/Popos/PersonData.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

namespace Morrislaptop\PopoFactory\Tests\Popos;

use Carbon\Carbon;

class PersonData
{
public function __construct(
public $firstName,
public string $lastName,
public string $email,
public string $homeAddress,
public string $companyName,
public ?string $companyName,
public string $workAddress,
public Carbon $dob,
public PersonDataDocBlock $spouse,
) {
}
Expand Down
34 changes: 34 additions & 0 deletions tests/Popos/PersonDataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Morrislaptop\PopoFactory\Tests\Popos;

use Morrislaptop\PopoFactory\PopoFactory;

/**
* @method PersonData make
*/
class PersonDataFactory extends PopoFactory
{
public static function factory(): static
{
return static::new(PersonData::class)->state([
'firstName' => 'Craig',
]);
}

public function gotNoJob()
{
return $this->state([
'companyName' => null,
]);
}

public function worksAtHome()
{
return $this->state(function ($attributes) {
return [
'workAddress' => $attributes['homeAddress'],
];
});
}
}

0 comments on commit 4dd8b6e

Please sign in to comment.