• Stars
    star
    533
  • Rank 83,238 (Top 2 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 12 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Enables the rapid creation of objects for testing

Factory Muffin 3.3

StyleCI Status Build Status Software License Latest Version Total Downloads

The goal of this package is to enable the rapid creation of objects for the purpose of testing.

It's basically a "factory_girl", simplified for use with PHP.

Installing

PHP 5.4+ and Composer are required.

In your composer.json, simply add "league/factory-muffin": "^3.3" to your "require-dev" section:

{
    "require-dev": {
        "league/factory-muffin": "^3.3"
    }
}

Faker support is provided by Factory Muffin Faker. If you want to enable faker support, then you need to add "league/factory-muffin-faker": "^2.3" too:

{
    "require-dev": {
        "league/factory-muffin": "^3.3",
        "league/factory-muffin-faker": "^2.3"
    }
}

Upgrading

It maybe be useful for existing users to check out the upgrade guide.

Usage

Introduction

This is the usage guide for Factory Muffin 3.0. Within this guide, you will see "the xyz function can be called". You should assume that these functions should be called on an instance of League\FactoryMuffin\FactoryMuffin; you should keep track of this instance yourself, and you can of course have multiple instances of this class for maximum flexibility. For simplicities sake, many of our examples include a $fm variable. This variable will actually be made available when files are required using the loadFactories function.

Model Definitions

You can define model factories using the define function. You may call it like this: $fm->define('Fully\Qualifed\ModelName')->addDefinitions('foo', 'bar'), where foo is the name of the attribute you want set on your model, and bar describes how you wish to generate the attribute. You may also define multiple attributes at once like this: $fm->define('Fully\Qualifed\ModelName')->setDefinitions('foo', 'bar'). Note that both functions append to the internal attributes definition array rather than replacing it. Please see the generators section for more information on how this works.

You can also define multiple different model definitions for your models. You can do this by prefixing the model class name with your "group" followed by a colon. This results in you defining your model like this: $fm->define('myGroup:Fully\Qualifed\ModelName')->addDefinitions('bar', 'baz'). You don't have to entirely define your model here because we will first look for a definition without the group prefix, then apply your group definition on top of that definition, overriding attribute definitions where required. Note that if you want to use group prefixes, you must also create a definition without the group prefix as well.

We have provided a nifty way for you to do this in your tests. PHPUnit provides a setupBeforeClass function. Within that function you can call $fm->loadFactories(__DIR__ . '/factories');, and it will include all files in the factories folder. Within those PHP files, you can put your definitions (all your code that calls the define function).

The loadFactories function will recurse through all sub-folders of the path you specify when searching for factory files, except for hidden folders (i.e. starting with a .) which will be ignored. It will also throw a League\FactoryMuffin\Exceptions\DirectoryNotFoundException exception if the factories directory you specify is not found.

Creation/Instantiation Callbacks

You may optionally specify a callback to be executed on model creation/instantiation using the setCallback function, like this: $fm->define('MyModel')->setCallback(function ($object, $saved) {}). We will pass your model instance as the first parameter to the callback and a boolean as the second parameter. The boolean will be true if the model is being persisted to the database (the create function was used), and false if it's not being persisted (the instance function was used). We're using the isPendingOrSaved function under the hood here. Note that if you specify a callback and use the create function, we will try to save your model to the database both before and after we execute the callback.

Generators

Callable

The callable generator can be used if you want a more custom solution. Whatever you return from your closure, or valid callable, will be set as the attribute. The closure/callable will be called with the same parameters as creation/instantiation callbacks described above: an instance of your model as the first parameter (to give you even more flexibility to modify it as you wish) and a boolean indicating if the model is being persisted to the database. In the following examples, we will go through using a closure, or callable, and then how to use faker to generate attributes.

Example 1

As you can see from this example, the ability to use a closure to generate attributes can be so useful and flexible. Here we use it to generate a slug based on the initially randomly generated 5 word long title.

$fm->define('MyModel')->setDefinitions([
    'title' => Faker::sentence(5),
    'slug' => function ($object, $saved) {
        $slug = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $object->title);
        $slug = strtolower(trim($slug, '-'));
        $slug = preg_replace("/[\/_|+ -]+/", '-', $slug);

        return $slug;
    },
]);
Example 2

This will set the foo attribute to whatever calling MyModel::exampleMethod($object, $saved) returns.

$fm->define('MyModel')->setDefinitions([
    'foo' => 'MyModel::exampleMethod',
]);
Example 3

There is a simple example of setting a few different attributes using our faker wrapper.

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->setDefinitions([
    'foo'    => Faker::word(),          // Set the foo attribute to a random word
    'name'   => Faker::firstNameMale(), // Set the name attribute to a random male first name
    'email'  => Faker::email(),         // Set the email attribute to a random email address
    'body'   => Faker::text(),          // Set the body attribute to a random string of text
    'slogan' => Faker::sentence(),      // Set the slogan attribute to a random sentence
]);
Example 4

This will set the age attribute to a random number between 20 and 40.

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->setDefinitions([
    'age' => Faker::numberBetween(20, 40),
]);
Example 5

This will set the name attribute to a random female first name. Because we've called the unique method first, the attribute should be unique between all your generated models. Be careful with this if you're generating lots models because we might run out of unique items. Also, note that calling Faker::setLocale('whatever') will reset the internal unique list.

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->addDefinition('name', Faker::unique()->firstNameFemale());
Example 6

This will set the profile_pic attribute to a random image url of dimensions 400 by 400. Because we've called the optional method first, not all the generated models will have an image url set; sometimes we will return null.

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->addDefinition('profile_pic', Faker::optional()->imageUrl(400, 400));
More

Check out the faker library itself to see all the available methods. There are far too many to cover in the documentation here, and far too many for them to cover in their documentation too. Note that you can fiddle with the underlying faker instance through the public methods on our faker class if you want.

Factory

The factory generator can be useful for setting up relationships between models. The factory generator will return the model id of the model you ask it to generate.

Example 1

When we create a Foo object, we will find that the Bar object will been generated and saved too, and it's id will be assigned to the bar_id attribute of the Foo model.

$fm->define('Foo')->addDefinition('bar_id', 'factory|Bar');

$fm->define('Bar')->addDefinition('baz', Faker::date('Y-m-d'));

Creating And Seeding

The create function will create and save your model, and will also save anything you generate with the Factory generator too. If you want to create multiple instances, check out the seed seed function, which accepts an additional argument at the start which is the number of models to generate in the process. The seed function will effectively be calling the create function over and over.

For example, let's create a user model and associate multiple location and email models to each one. Each email will also have multiple token models.

$user = $fm->create('User');
$profiles = $fm->seed(5, 'Location', ['user_id' => $user->id]);
$emails = $fm->seed(100, 'Email', ['user_id' => $user->id]);

foreach ($emails as $email) {
    $tokens = $fm->seed(50, 'Token', ['email_id' => $email->id]);
}

You may encounter the following exceptions:

  • League\FactoryMuffin\Exceptions\ModelNotFoundException will be thrown if the model class defined is not found.
  • League\FactoryMuffin\Exceptions\DefinitionNotFoundException will be thrown if you try to create a model and you haven't defined a model definition for it earlier.
  • League\FactoryMuffin\Exceptions\SaveFailedException will be thrown if the save function on your model returns false.
  • League\FactoryMuffin\Exceptions\SaveMethodNotFoundException will be thrown if the save function on your model does not exist.
  • Any other exception thrown by your model while trying to create or save it.

There are 5 other helper functions available:

  • You may call pending to return an array of all the objects waiting to be saved.
  • You may call isPending with an instance of a model to check if will be saved.
  • You may call saved to return an array of all the saved objects.
  • You may call isSaved with an instance of a model to check if it's saved.
  • You may call isPendingOrSaved with an instance of a model to check if it will be saved, or is already saved.

Also, a reminder that the instance function is still available if you don't want database persistence.

Deleting

You can delete all your saved models with the deleteSaved function. Please note that your saved models will be deleted in the reverse order they were saved to ensure relationships behave correctly.

If one or more models cannot be deleted, a League\FactoryMuffin\Exceptions\DeletingFailedException will be raised after we have attempted to delete all the saved models. You may access each underlying exception, in the order they were thrown during the whole process, with the getExceptions function which will return an array of exceptions. You may encounter the following exceptions:

  • League\FactoryMuffin\Exceptions\DeleteFailedException will be thrown if the delete function on your model returns false.
  • League\FactoryMuffin\Exceptions\DeleteMethodNotFoundException will be thrown if the delete function on your model does not exist.
  • Any other exception thrown by your model while trying to delete it.

It's recommended that you call the deleteSaved function from PHPUnit's tearDownAfterClass function, however, if you are writing tests using Laravel's TestCase, you should call the deleteSaved function from the tearDown method before calling parent::tearDown. This method flushes the application instance's bindings and Factory Muffin would not unable to execute its deletes. Further more, this unbinds the assigned exception handler and you will not be able to troubleshoot your tests due to binding resolution exceptions obfuscating the true exceptions.

Real Examples

To start with, we need to create some definitions:

# tests/factories/all.php

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('Message')->setDefinitions([
    'user_id'      => 'factory|User',
    'subject'      => Faker::sentence(),
    'message'      => Faker::text(),
    'phone_number' => Faker::randomNumber(8),
    'created'      => Faker::date('Ymd h:s'),
    'slug'         => 'Message::makeSlug',
])->setCallback(function ($object, $saved) {
    // we're taking advantage of the callback functionality here
    $object->message .= '!';
});

$fm->define('User')->setDefinitions([
    'username' => Faker::firstNameMale(),
    'email'    => Faker::email(),
    'avatar'   => Faker::imageUrl(400, 600),
    'greeting' => 'RandomGreeting::get',
    'four'     => function() {
        return 2 + 2;
    },
]);

You can then use these factories in your tests:

# tests/TestUserModel.php

use League\FactoryMuffin\Faker\Facade as Faker;

class TestUserModel extends PHPUnit_Framework_TestCase
{
    protected static $fm;

    public static function setupBeforeClass()
    {
        // create a new factory muffin instance
        static::$fm = new FactoryMuffin();

        // you can customize the save/delete methods
        // new FactoryMuffin(new ModelStore('save', 'delete'));

        // load your model definitions
        static::$fm->loadFactories(__DIR__.'/factories');

        // you can optionally set the faker locale
        Faker::setLocale('en_EN');
    }

    public function testSampleFactory()
    {
        $message = static::$fm->create('Message');
        $this->assertInstanceOf('Message', $message);
        $this->assertInstanceOf('User', $message->user);
    }

    public static function tearDownAfterClass()
    {
        static::$fm->deleteSaved();
    }
}

Further Information

If you want more information, the following resources are available to you:

Integrations

Contributing

Please check out our contribution guidelines for details.

Credits

Factory Muffin is based on Zizaco Zizuini's original work on "Factory Muff", and is currently maintained by Graham Campbell. Scott Robertson was also a co-maintainer before the 3.0 release. Thank you to all our wonderful contributors too.

Security

If you discover a security vulnerability within this package, please send an email to Graham Campbell at [email protected]. All security vulnerabilities will be promptly addressed. You may view our full security policy here.

License

Factory Muffin is licensed under The MIT License (MIT).

For Enterprise

Available as part of the Tidelift Subscription

The maintainers of league/factory-muffin and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

More Repositories

1

flysystem

Abstraction for local and remote filesystems
PHP
13,354
star
2

oauth2-server

A spec compliant, secure by default PHP OAuth 2.0 Server
PHP
6,362
star
3

omnipay

A framework agnostic, multi-gateway payment processing library for PHP 5.6+
PHP
5,813
star
4

fractal

Output complex, flexible, AJAX/RESTful data structures.
PHP
3,524
star
5

oauth2-client

Easy integration with OAuth 2.0 service providers.
PHP
3,508
star
6

csv

CSV data manipulation made easy in PHP
PHP
3,337
star
7

commonmark

Highly-extensible PHP Markdown parser which fully supports the CommonMark and GFM specs.
PHP
2,738
star
8

glide

Wonderfully easy on-demand image manipulation library with an HTTP based API.
PHP
2,550
star
9

climate

PHP's best friend for the terminal.
PHP
1,867
star
10

html-to-markdown

Convert HTML to Markdown with PHP
PHP
1,619
star
11

flysystem-aws-s3-v3

[READYONLY SUB-SPLIT]Flysystem Adapter for AWS SDK V3
PHP
1,557
star
12

skeleton

A skeleton repository for League Packages
PHP
1,525
star
13

event

Event package for your app and domain
PHP
1,519
star
14

plates

Native PHP template system
PHP
1,470
star
15

geotools

Geo-related tools PHP 7.3+ library built atop Geocoder and React libraries
PHP
1,366
star
16

color-extractor

Extract colors from an image like a human would do.
PHP
1,297
star
17

mime-type-detection

League Mime Type Detection
PHP
1,262
star
18

uri

[READ-ONLY] URI manipulation Library
PHP
1,032
star
19

pipeline

League\Pipeline
PHP
959
star
20

oauth1-client

OAuth 1 Client
PHP
936
star
21

tactician

A small, flexible command bus
PHP
858
star
22

container

Small but powerful dependency injection container
PHP
843
star
23

period

PHP's time range API
PHP
720
star
24

route

Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute.
PHP
651
star
25

iso3166

A PHP library providing ISO 3166-1 data.
PHP
639
star
26

openapi-psr7-validator

It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications
PHP
525
star
27

config

Simple yet expressive schema-based configuration library for PHP apps
PHP
478
star
28

uri-interfaces

League URI Interfaces
PHP
459
star
29

shunt

[ABANDONED] PHP library for executing commands on multiple remote machines, via SSH
PHP
436
star
30

oauth2-google

Google Provider for the OAuth 2.0 Client
PHP
396
star
31

uri-parser

RFC3986/RFC3987 compliant URI parser
PHP
394
star
32

flysystem-bundle

Symfony bundle integrating Flysystem into Symfony applications
PHP
361
star
33

flysystem-cached-adapter

Flysystem Adapter Cache Decorator.
PHP
356
star
34

statsd

A library for working with StatsD
PHP
351
star
35

url

A simple PHP library to parse and manipulate URLs
PHP
347
star
36

booboo

A modern error handler capable of logging and formatting errors in a variety of ways.
PHP
338
star
37

omnipay-common

Core components for the Omnipay PHP payment processing library
PHP
330
star
38

monga

Simple and swift MongoDB abstraction.
PHP
328
star
39

flysystem-sftp

[READ-ONLY SUBSPLIT] Flysystem Adapter for SFTP
PHP
308
star
40

uri-components

[READ-ONLY] League URI components objects
PHP
307
star
41

omnipay-paypal

PayPal driver for the Omnipay PHP payment processing library
PHP
299
star
42

oauth2-facebook

Facebook Provider for the OAuth 2.0 Client
PHP
299
star
43

tactician-bundle

Bundle to integrate Tactician with Symfony projects
PHP
245
star
44

uri-schemes

Collection of URI Immutable Value Objects
PHP
216
star
45

uri-manipulations

Functions and Middleware to manipulate URI Objects
PHP
199
star
46

uri-hostname-parser

A lightweight hostname parser according to public suffix list ICANN section
PHP
197
star
47

omnipay-stripe

Stripe driver for the Omnipay PHP payment processing library
PHP
184
star
48

oauth2-server-bundle

Symfony bundle for the OAuth2 Server.
PHP
183
star
49

json-guard

Validation of json-schema.org compliant schemas.
PHP
175
star
50

flysystem-local

PHP
152
star
51

commonmark-ext-table

The table extension for CommonMark PHP implementation
PHP
128
star
52

glide-laravel

Glide adapter for Laravel
PHP
121
star
53

oauth2-github

GitHub Provider for the OAuth 2.0 Client
PHP
109
star
54

flysystem-ziparchive

Flysystem Adapter for ZipArchive's
PHP
101
star
55

omnipay-example

Example application for Omnipay PHP payments library
PHP
100
star
56

glide-symfony

Glide adapter for Symfony
PHP
96
star
57

oauth2-linkedin

LinkedIn Provider for the OAuth 2.0 Client
PHP
83
star
58

flysystem-memory

Flysystem Memory Adapter
PHP
75
star
59

tactician-container

Load Tactician handlers from any PSR-11/container-interop container
PHP
75
star
60

stack-attack

StackPHP Middleware based on Rack::Attack
PHP
74
star
61

flysystem-webdav

[READ ONLY] WebDAV adapter for Flysystem
PHP
70
star
62

flysystem-dropbox

Flysystem Adapter for Dropbox [ABANDONED] replacement: https://packagist.org/packages/spatie/flysystem-dropbox
PHP
67
star
63

stack-robots

StackPHP middleware providing robots.txt disallow for non-production environments
PHP
67
star
64

oauth2-instagram

Instagram Provider for the OAuth 2.0 Client
PHP
65
star
65

tactician-logger

Adds PSR-3 logging support to the Tactician Command Bus
PHP
62
star
66

omnipay-mollie

Mollie driver for the Omnipay PHP payment processing library
PHP
62
star
67

di

An Ultra-Fast Dependency Injection Container. DEPRECATED
PHP
58
star
68

tactician-doctrine

Tactician plugins for the Doctrine ORM, primarily transactions
PHP
57
star
69

omnipay-authorizenet

Authorize.Net driver for the Omnipay payment processing library
PHP
57
star
70

omnipay-sagepay

Sage Pay driver for the Omnipay PHP payment processing library
PHP
55
star
71

flysystem-azure-blob-storage

PHP
53
star
72

flysystem-aws-s3-v2

Flysystem Adapter for AWS SDK V2
PHP
50
star
73

DEPRECATED-squery

PHP wrapper for osquery
PHP
49
star
74

phpunit-coverage-listener

Report code coverage statistics to third-party services
PHP
48
star
75

thephpleague.github.io

The League of Extraordinary Packages website
SCSS
45
star
76

construct-finder

PHP code construct finder
PHP
42
star
77

factory-muffin-faker

A wrapper around faker for factory muffin
PHP
40
star
78

uri-query-parser

a parser and a builder to work with URI query string the right way in PHP
PHP
37
star
79

flysystem-rackspace

Flysystem Adapter for Rackspace
PHP
37
star
80

flysystem-azure

Flysystem adapter for the Windows Azure.
PHP
35
star
81

flysystem-sftp-v3

PHP
35
star
82

omnipay-braintree

Braintree Driver for Omnipay Gateway
PHP
34
star
83

json-reference

A library for working with JSON References.
PHP
33
star
84

uri-src

URI manipulation Library
PHP
29
star
85

commonmark-extras

Useful extensions for the league/commonmark parser
PHP
28
star
86

uploads

Receive, validate, and distribute uploaded files.
PHP
27
star
87

object-mapper

PHP
27
star
88

omnipay-dummy

Dummy driver for the Omnipay PHP payment processing library
PHP
26
star
89

flysystem-replicate-adapter

Flysystem Adapter Decorator for Replicating Filesystems.
PHP
24
star
90

omnipay-paymentexpress

PaymentExpress driver for the Omnipay PHP payment processing library
PHP
24
star
91

omnipay-worldpay

WorldPay driver for the Omnipay PHP payment processing library
PHP
23
star
92

flysystem-ftp

[SUB-SPLIT] Flysystem FTP Adapter
PHP
23
star
93

flysystem-async-aws-s3

PHP
22
star
94

omnipay-migs

MIGS driver for the Omnipay PHP payment processing library
PHP
21
star
95

omnipay-payfast

PayFast driver for the Omnipay PHP payment processing library
PHP
21
star
96

flysystem-google-cloud-storage

PHP
20
star
97

omnipay-firstdata

First Data driver for the Omnipay PHP payment processing library
PHP
20
star
98

omnipay-multisafepay

MultiSafepay driver for the Omnipay PHP payment processing library
PHP
19
star
99

flysystem-gridfs

GridFS Adapter for Flysystem
PHP
19
star
100

tactician-bernard

Tactician integration with the Bernard queueing library
PHP
19
star