Factory Muffin

Enable the rapid creation of objects for the purpose of testing.

What is Factory Muffin?

Factory Muffin allows us to create objects easily, and consistently when writing tests. These objects will automatically be populated with the data types we specify.

Where does the data come from?

Factory Muffin uses a popular library called Faker to generate random data such as strings, email addresses, phone numbers, credit card numbers and more.

What is Faker?

Faker is a PHP library that generates fake data for you. Whether you need to bootstrap your database, create good-looking XML documents, fill-in your persistence to stress test it, or anonymize data taken from a production service, Faker is for you.

Faker Formatters

There are hundreds of Faker Formatters. These are used to return the random data needed to populate the Factory object. Some examples are:

->catchPhrase; // Monitored regional contingency
->tld; // .com
->ipv4; // 109.133.32.252
->userAgent; // Mozilla/5.0 (Windows CE) AppleWebKit/5350 (KHTML, like Gecko) Chrome/13.0.888.0 Safari/5350
->imageUrl(640, 480); // http://lorempixel.com/640/480/
->titleMale; // Mr.
->creditCardDetails; // array('MasterCard', '4485480221084675', 'Aleksander Nowak', '04/13')
->hexcolor; // #fa3cc2
->mimeType; // video/x-msvideo
->uuid; // 7e57d004-2b97-0e7a-b45f-5387367791cd

You can find a list of them all by looking at the Faker documentation.

Factories

Before we can use Factory Muffin in our tests, we need to define the data we want to be generated. To do this, we create "factories" in the following way:

FactoryMuffin::define(
  'User',
  array(
    'username' => 'string',
    'password' => 'string',
    'email'    => 'email',
    'address'  => 'streetAddress',
  )
);

These files are generally placed in the root of your "tests" directory. For example: '/tests/factories/user.php'.

Including Factories

You must tell FactoryMuffin to load these factories

public static function setupBeforeClass()
{
  FactoryMuffin::setSaveMethod('save');
  FactoryMuffin::loadFactories(__DIR__ . '/factories');
}

Using "setSaveMethod" allows us to modify which function is used when saving the objects to the DB.

Using Factories in Tests

Now that our definitions have been created, we can go ahead and use these to create objects in our tests.

function testNetworkHasDescription()
{
  $user = FactoryMuffin::instance('User');

  /**
   * User Object
   * (
   *   [username] => qdfaaapozu
   *   [password] => ipbwhlvvzq
   *   [email] => hagenes.bo@mayert.com
   *   [address] => 6654 Howe Point
   * )
   */
}

Because we are calling ::instance(), FactoryMuffin will not try and persist this to the DB.

Saving the Factory to the DB

Factory Muffin can attempt to persist these objects to the DB.

function testNetworkHasDescription()
{
  $user = FactoryMuffin::create('User');
}

You do this by using ::create(). We do this by calling ->save() on the object after creating it.

Overwriting Values

When calling create/instance, you are able to overwrite values set in the definitions file.

function testOverwritingNetwork()
{
  $user = FactoryMuffin::instance('User', [
    'username' => 'scott'
  ]);

  /**
   * User Object
   * (
   *   [username] => scott
   *   [password] => mhygetwsfj
   *   [email] => theodore61@wisoky.biz
   *   [address] => 7091 Luis Row
   * )
   */
}

Factory Inception

You can have a factory create a factory within itself, and capture the ID to store in the DB

FactoryMuffin::define(
  'Profile',
  array(
    'profile'  => 'text',
  )
);
FactoryMuffin::define(
  'User',
  array(
    'username' => 'string',
    'password' => 'string',
    'email'    => 'email',
    'address'  => 'streetAddress',
    'profile_id'  => 'factory|Profile'
  )
);

Using Factory Inception

By creating a User factory, we will now go ahead and create the Profile factory, and return its ID automatically

function testFactoryInception()
{
  $user = FactoryMuffin::create('User');
  $profile = Profile::find($user->profile_id);

  /**
   * Profile Object
   * (
   *   [profile] => Aut optio omnis quia voluptatem.
   * )
   */

}

Defining Static Methods

You can have Factory Muffin call static methods while creating the data sets.

FactoryMuffin::define(
  'User',
  array(
    'username' => 'string',
    'hash' => 'call|makeHash|string'
  )
);
class User
{
  public static function makeHash($seed)
  {
    return hash('sha256', $seed);
  }
}

Notice that we are passing a randomly generated string into ::makeHash()

Using Static Methods

And now, when we create the Factory we are provided with a random hash too

function testFactoryInception()
{
  $user = FactoryMuffin::create('User');

  /**
   * User Object
   *(
   *  [username] => nxqgclrhgk
   *  [hash] => d7e58c93348905ca451ad44c6b22407444e19d32fc59d597d863373196341bfc
   * )
   */
}

Tracked Objects

All objects that are saved to the DB are tracked.

function tearDownAfterClass()
{
  $objects = FactoryMuffin::saved();
}

This will return an array of all saved objects

Cleaning Up

You can remove all tracked objects in one go.

function tearDownAfterClass()
{
  FactoryMuffin::setDeleteMethod('delete');
  FactoryMuffin::deleteSaved();
}

This will loop over all of the objects and call ->delete() on them.

Questions?