Home » Code » Laravel 4 Fixture Replacement with Factory Muffin

Laravel 4 Fixture Replacement with Factory Muffin

Posted by on May 27th, 2013

Laravel 4 Fixture Replacement with FactoryMuff
Last week we started looking at Testing Laravel 4 Models. The validation tests we wrote for the User model are really easy to write because we are only testing very simply functionality. However, in the grand scheme of a full application, we are going to have to write some much more intricate tests.

One such example of this is when we are testing the relationships between Models. For example, in Cribbb, each Post belongs to a User. In order to create a Post, we need an instance of a User. In order to quickly instantiate an instance of a User, we can use a Fixture Replacement.

In this tutorial I’m going to be looking at using Fixture Replacement to quickly create instances of other Models within our tests.

Update: The following tutorial has been updated to reflect the 2.0 release of FactoryMuff (now called Factory Muffin).

What is Fixture Replacement?

Fixture Replacement is a way to quickly create instances of Model objects. So instead of actually having to create an instance of a certain Model, Fixture Replacement can quickly create one for you.

So instead of writing this each time you want to create an instance of the User model:

$user = new User;
$user->username = "philipbrown";
$user->email = "name@domain.com";
$user->password = "password";
$user->password_confirmation = "password";

You can simply write:

$user = FactoryMuffin::create('User');

The $user Model object will be a real instance of the User Model filled with real test data, but it saves you from actually having to create it yourself.

The real power of Fixture Replacement is when you have Models that are related to other Models. For example, in order to create a Post, we need a User object. When you create an instance of a Model object using Fixture Replacement, the Model relationships are also created too. This makes Fixture Replacement perfect for writing automated tests.

What is Factory Muffin?

Factory Muffin is a Fixture Replacement package that allows you to quickly create instances of Models. Factory Muffin is actually based on the very popular factory_girl gem, which I really like.

Installing Factory Muffin

Factory Muffin is available as a package through Composer. To install Factory Muffin, add the following line to your composer.json file:

{
  "require-dev": {
    "league/factory-muffin": "~2.0"
  }
}

Next, run the following command from the terminal to pull the package into your project:

composer update

Defining your fixtures

Before we can start using our fixtures in our tests, first we have to define a model factory so Factory Muffin knows exactly how you want your model object to look.

In order to create a factory, we can pass the model name and an array of attributes to the define() method on the Factory Muffin Facade:

League\FactoryMuffin\Facade::define('User', array());

The second argument is an array where you can specify how you want the properties of the model to be filled.

For example, if we wanted to set a user’s name, we could write the following:

League\FactoryMuffin\Facade::define('User', array(
  'name' => 'firstName'
));

If the users of our application can optionally have a profile picture, then we will want to be able to create fixtures that sometimes have a profile picture set. You can define this behaviour like this:

League\FactoryMuffin\Facade::define('User', array(
  'name' => 'firstName',
  'profile_pic' => 'optional:imageUrl|400;400'
));

Factory Muffin’s fake data is generated via the excellent Faker package. I’ve used Fake in a number of projects to generate fake data for populating databases, it is truly a fantastic package. To see the full spectrum of the fake data you can generate I would urge you to have a look at the GitHub repository.

Defining relationships

As I mentioned in the introduction to this post, I find fixtures to be incredibly useful when I need to create an entity as well as it’s related entities.

For example, I need to create a Post object and it’s related User object, it would be useful if I could do that automatically, rather than manually creating and saving the two objects in the test.

Fortunately Factory Muffin can handle this for us.

To define a relationship in Factory Muffin, simply specify the factory option and pass the name of the related entity.

League\FactoryMuffin\Facade::define('Post', array(
  'user_id' => 'factory|User'
));

League\FactoryMuffin\Facade::define('User', array(
  'name' => 'firstName',
  'profile_pic' => 'optional:imageUrl|400;400'
));

Now if you create a new Post instance, the returned object will automatically have an instance of User through the relationship.

Using Factory Muffin in your tests

Now that we know how to define our fixtures, we are ready to start using them in our tests.

Create a new file under tests called tests/factories/all.php and copy the following code:

use League\FactoryMuffin\Facade as FactoryMuffin;

FactoryMuffin::define('Post', array(
  'user_id' => 'factory|User'
));

FactoryMuffin::define('User', array(
  'name' => 'firstName',
  'profile_pic' => 'optional:imageUrl|400;400'
));

Note: I’m aliasing the Facade to FactoryMuffin in the example above.

Create a new test file under tests called UserTest.php and copy the following code:

class UserTest extends PHPUnit_Framework_TestCase {

}

The first thing we need to do is to load our factories so Factory Muffin knows how to generate new model instances:

public static function setupBeforeClass()
{
  \League\FactoryMuffin\Facade::loadFactories(__DIR__ . '/factories');
}

The setupBeforeClass() method will automatically include our factory definitions earlier so they are available during the tests.

We can also define a tearDownAfterClass() method to clean up the objects that were created:

public static function tearDownAfterClass()
{
  \League\FactoryMuffin\Facade::deleteSaved();
}

Now we can start using the fixtures in our tests:

public function testCreateNewPost()
{
  $post = \League\FactoryMuffin\Facade::create('Post');
  $this->assertInstanceOf('Post', $post);
  $this->assertInstanceOf('User', $post->user);
}

Conclusion

And there you have it, using Factory Muffin, we can dramatically reduce the amount of code we have to write when writing tests. Fixture replacement makes quickly creating instances of models really easy, and it will save you a lot of hassle in the long run.

You will probably find that you require fixtures quite a bit when you are testing your application as a whole. Being able to very quickly generate model objects and their related entities is a blessing as it means you can create realistic objects on-the-fly without having to deal with that “infrastructure” in your tests.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

To view a full listing of the tutorials in this series, click here.

Philip Brown

Hey, I'm Philip Brown, a designer and developer from Durham, England. I create websites and web based applications from the ground up. In 2011 I founded a company called Yellow Flag. If you want to find out more about me, you can follow me on Twitter or Google Plus.

  • mckendricks

    Great intro to FactoryMuff. I Wanted to suggest that password_confirmation should include same:password as part of the validation rules to ensure that the password was actually confirmed.

    • Thank you :)

      I like to use confirmed like this:

      public static $rules = array(
      'password' => 'required|min:8|confirmed',
      'password_confirmation' => 'required|min:8',
      );

      But that is also a great suggestion of how to achieve the desired result.

      • gin93r

        I’m having a similar issue. I added an email confirmation and I get a SaveException: “The email confirmation does not match.” – Could not save the model of type: User

        public static $rules = array(
        ‘fullname’=>’required’,
        ’email’=>’required|email|confirmed’,
        ’email_confirmation’=>’required|email’,
        ‘password’=>’required|alpha_num|min:8|confirmed’,
        ‘password_confirmation’=>’required|alpha_num|min:8’
        );

        public static $factory = array(
        ‘fullname’=>’string’,
        ’email’ => ’email’,
        ’email_confirmation’=>’email’,
        ‘password’=>’password’,
        ‘password_confirmation’=>’password’
        );

        • Yeah that’s exactly right. FactoryMuff will generate unique strings, so you just have to manually set a static email address.

          • gin93r

            That solved the email matching problem. Though it’s still giving me a failure when saving.

            $user = FactoryMuff::create(‘User’);
            $this->assertTrue($user->save());

            It gives me “Failed asserting that false is true.”

            I did a few assertEquals tests to see if the fullname, email, password are setting correctly and all are.

          • Try dumping out $user->save(). It should tell you what’s wrong.

  • Amodef

    Hi and thanks for this great tutorial!

    On your validation list, shouldn’t you add a unique property to the email address ?

    I’ve done this on my own project, but get problems with the update form (that should not consider the unchanged email address as a duplicate). It would be great to see how you deal with that (and with empty passwords fields on update too).

    Thanks in advance!

    • Hi Amodef, you are absolutely right, I should of added a unique property to the email address. I’ll update it in Cribbb soon, thank you!

      Yeah, I’ve had that problem too. Unfortunately Ardent doesn’t recognise whether this is new record or updating an existing record. The easiest way to solve this issue is to use a beforeSave hook to force Ardent to use only the validation that you require in this instance.

      Have a look at the documentation for those hooks.
      https://github.com/laravelbook/ardent#overriding-beforesave-and-aftersave

      You have actually caught something that I was going to write an article for :p I haven’t written it yet, but I’m going to look to either fork Ardent or write something so this should hopefully work better without having to do anything.

      • Amodef

        Here is how I’ve dealt with this problem (without using Ardent):
        https://gist.github.com/amodef/5705553

        Hope it helps…

        • Very interesting!

          I’ve also ripped out Ardent and created my own model to extend from so I can keep validation in the model and not have any logic in the controller. Ardent is nice, but sometimes you’ve just gotta start from scratch to get it to work how you want.

          I was actually looking to do something similar to your approach where you use Input::get('_method') but instead I’m using the $this->exists property to detect if the record is being update :)

  • Unfortunately i do not get phpunit success messages anymore. For the first tests i got them, but not anymore. Strange right? Now i just see:

    PHPUnit 3.7.21 by Sebastian Bergmann.

    Configuration read from /Applications/MAMP/htdocs/cribbb/phpunit.xml

    • Hmm that is strange. Did you change anything?

      • Joel Brubaker

        I had this problem and it was a small typo (leaving off the semi-colon at the end).

  • Pingback: Creating a Laravel 4 package | Culttt()

  • Clay

    In the above example where you say
    “The first test we’re going to look at simply proves that FactoryMuff is working correctly:” on line 9 is this correct? (..$post->user->id)

    $this->assertEquals($post->user_id, $post->user->id);

    if so, I’m getting an error:
    1) PostTest::testRelationshipWithUser
    ErrorException: Trying to get property of non-object

    Any idea why?

    • Sounds like FactoryMuff isn’t creating your object correctly. Does you factory meet the requirements of your model? Try dumping out the $post object to see what’s going on.

      • Clay

        When I dump the $post object everything looks fine, but when I dump $post->user it returns NULL..

        • Hmm, that’s weird. You definitely have 'user_id' => 'factory|User', in your factory and the correct models and relationships set up?

          • Clay

            Yes, I have followed all directions including setting up the models and adding ‘user_id’ => ‘factory|User’, i thought this line also creates the relationship for you. Am I missing something?

          • Clay

            I have solved it! I don’t think you included the syntax for adding the relationship in the Post model. I added:

            public function user()
            {
            return $this->belongsto(‘User’);
            }

            Now everything works fine, thanks once again!

          • Excellent! Yeah that makes sense! Glad you got it sorted :)

      • Clay

        I updated to php 5.5.3 and phpunit 3.8.*@dev and now it’s the same error plus: The Xdebug extension is not loaded. No code coverage will be generated. IDK what’s going on.. It must be something wrong with my environment.. Thanks for your help!

  • dstewart101

    Hi – Really enjoyinging your tutorials, but I’ve hit a snag… hopefully if you have a moment you can help me? At this part

    “Creating a User using FactoryMuff

    Now we can test that the body field is required by creating a User using FactoryMuff and attempting to save without the body being set.”

    It seems to create a Post record in my database just fine and therefore I’m getting a failure:

    There was 1 failure:

    1) PostTest::testUserIdIsRequired

    Failed asserting that true is false.

    This matches up with the part:

    “// Post should not save
    $this->assertFalse($post->save());”

    It seems my test is creating a user to match the creation of a Post??? Any thoughts on this one? Thanks.

    • Thanks :)

      Do you have the user_id set as a required field in your Post model? Sounds like that’s the issue.

      • dstewart101

        thanks for replying… I think it’s all there… here is my post model (sorry about the formatting…)

        class Post extends Eloquent {
        protected $fillable = array(‘body’);
        public function user() {
        return $this->belongsTo(‘User’);
        }

        /*** Ardent validation rules */

        public static $rules = array(
        ‘body’ => ‘required’,
        ‘user_id’ => ‘required’
        );

        /*** Factory */

        public static $factory = array (
        ‘body’ => ‘text’,
        ‘user_id’ => ‘factory|User’,
        );

        }

        Stuck :(
        Thanks again.

        • Hmm yeah that does look right. Push your project up to github so I can clone it and I’ll take a look for you.

          • dstewart101

            Really? That’s very kind.
            I’ll do that this evening. Let you know via twitter? Thanks.
            DS

          • I had an epiphany in the car this morning. I think your validation isn’t working correctly because you need to extend Ardent not Eloquent.

            Your test is passing because it is not recognising any validation rules.

            Try that and let me know how you get on.

          • dstewart101

            Superstar! Yes indeed. Wrong extends… thanks so much for this. Really appreciate that. Great to see someone who not only blogs but backs up the punters coming to read it. keep up the good work.

          • No problem, glad you got it sorted :)

          • elPhissher

            I had the same issue, had to change extend to Ardent from Eloquent as well. I didn’t change it from the previous tutorial when installing Ardent.

          • Excellent :) Glad you got it sorted :D

  • dstewart101

    Hi again –

    I’m back revisiting this material for a second pass – this time using it as a guide to write my own stuff rather than follow a tutorial to the letter. I’ve got an odd “problem”….

    When I’m testing my user model with the fixture replacement I get the error message:

    1) UserTest::testSaveUser
    Failed asserting that false is true.

    Basically I’m saving a user record to my database, and I want to test this happens. It is happening, despite the error message telling me that it has failed. Have you come across anything like this before?

    Thanks again for these great guides.

    DS

    • Hmm, so the record is saving, but it’s returning false? Are you sure it is saving correctly? Have you dumped the user to check?

      • David Stewart

        Hi Philip – yeah exactly that!
        All seems to be well. It saves fine and runs without error when I use specific values for each of the name value pairs in the model, but gives an error message and stills saves to the database when I use FactoryMuff… so I figured it must be to do with the factory. Maybe I’ve been looking at it too long. Here is some code if it helps. I appreciate you taking the time.

        from User model

        public static $factory = array(
        ‘username’ => ‘string’,
        ’email’ => ’email’,
        ‘password’ => ‘password’,
        ‘password_confirmation’ => ‘password’
        );

        and UserTest.php

        use ZizacoFactoryMuffFacadeFactoryMuff;

        class UserTest extends TestCase {
        public function testFixedUserIsSaved() {
        $user = new User;
        $user->username = “ds_user”;
        $user->email = “ds@ds.com”;
        $user->password = “password”;
        $user->password_confirmation = “password”;
        $this->assertTrue($user->save());
        }

        public function testFactoryUserIsSaved() {
        $user = FactoryMuff::create(‘User’);
        $this->assertTrue($user->save());
        }
        }

        Fixed user goes into db fine and no error, factory user goes in fine but the error message is:
        1) UserTest::testFactoryUserIsSaved
        Failed asserting that false is true.

        Argh!
        Again – thanks for looking.

        DS

        • Hmm, do you have constraints on the database columns or auto hydrating validation on your model?

          I would try dumping the $user variable from Factory test before and after the save() method is called so you can see what is attempting to be saved or what errors are being passed back.

          Do you have your code on GitHub? I can take a look if you want?

          • dstewart101

            Well thanks again! I have put it up at https://github.com/dstewart101/problems

            I’d be really grateful if you had the time to look at it.

            Thanks!

          • It’s because you don’t have to save an object that is created by FactoryMuff because FactoryMuff already creates it. So when you’re trying to run the save method it’s not actually saving, because the object already exists.

            Hope that helps :)

          • dstewart101

            Erm… doesn’t the save() method actually do the saving to the database? I have save() method used in other test files (e.g. GameWorldTest.php) with the same kind of testing template and I don’t get this problem. Sorry – I don’t quite understand your answer. Thanks. DS

          • Yeah, but you don’t need to use it on a FactoryMuff object.

            It looks like it’s because of the password_confirmation field. If you remove that field it works. It must be a conflict between FactoryMuff and Ardent.

            To be honest I wouldn’t spend time trying to write tests to verify that FactoryMuff is working. That’s the responsibility of the author of FactoryMuff so you don’t need to worry about it.

          • dstewart101

            Hi Philip – yes, I narrowed it down to the password_confirmation myself too. Glad I was on the right track. You are right about the responsibility thing of course! I just like to test things to death to understand them. I appreciate the help once again. Thanks a million.

          • No problem :) Let me know if you want to ask any more questions!

  • TugsMara

    Hi, Philip! Thanks for the very informative article. Does FactoryMuff require that tables being tested have timestamps? I’m getting a “table has no column named updated_at” error.

  • Johan Nyberg

    Hi Philip, and thanks for a great tutorial!

    When I try to run phpunit after copying your function testUserIdIsRequired the test fails, returning this error:

    1) PostTest::testUserIdIsRequired

    IlluminateDatabaseQueryException: SQLSTATE[HY000]: General error: 1364 Field ‘user_id’ doesn’t have a default value (SQL: insert into `posts` (`body`, `updated_at`, `created_at`) values (Yada yada yada, 2014-03-15 20:54:24, 2014-03-15 20:54:24))

    Any ideas?

    • You haven’t got a validation requirement for the user_id field so the database is complaining instead.

      • Johan Nyberg

        Nah, I found it. I had the same issues as the others had with the add_username migration. Once I got rid of that and added the username to the create-migration I got it working.

        Thanks again for a great tutorial!

  • Johan Nyberg

    Sorry, have another one.. :) When I try to run the testPostBodyIsRequired, I get this:

    SQLSTATE[23000]: Integrity constraint violation: 19 posts.body may not be NULL (SQL: insert into “posts” (“user_id”, “updated_at”, “created_at”) values (1, 2014-03-18 19:34:36, 2014-03-18 19:34:36))

    I have copied your code line by line:

    public function testUserIdIsRequired()
    {
    // Create new Post
    $post = new Post;

    // Set the boy
    $post->body = “Yada yada yada”;

    // Post should not save
    $this->assertFalse($post->save());

    // Save the errors
    $errors = $post->errors()->all();

    // There should be 1 error
    $this->assertCount(1, $errors);

    // The error should be set
    $this->assertEquals($errors[0], “The user id field is required.”);
    }

    • Looks like the same issue I had. My fix was to make the `username` column accept NULL values. In the “AddUsernameToUsersTable” migration class I added this:

      $table->string(‘username’)->nullable();

  • Phil’s Head

    hi

  • Pingback: Using Events in Laravel 4 | Culttt()

  • Rafael Borges

    Hello Philip, this is by far the best tutorial on Laravel, and I appreciate your time on writing this. I have a question on your testRelationshipWithUser() though. So just for the sake of learning, I’m implementing your solution, and somehow I’m getting this error when executing phpunit:

    There was 1 error:

    1) PostTest::testRelationshipWithUser

    ErrorException: Trying to get property of non-object

    I’ve pretty much followed everything you did so far. Do you know why this is happening and how to fix it? Again, appreciate your time!

    • Rafael Borges

      I just saw Clay had the same problem… I was able to fix it by using his method… thank you anyway!

  • Hey,

    We have renamed/moved the package (and thus, class names), and v2 will be coming out soon. Once it does, do you think you would be able to update the tutorial?

    https://github.com/thephpleague/factory-muffin/

    Thanks
    Scott

  • Nirmal Thapa

    Philip,

    when I try to to test UserTest file, I am getting a Fatal error. I have followed exactly like yours.

    But when I run the test I am getting,

    Fatal error: call to a member function make() on a non-object in C:………laraveframeworksrcIlluminateSupportFacadesFacade.php on line 214.

    I might have done some simple mistake but cannot sort it out.

    (I have files on https://github.com/nirmalz/learnLaravel, If you have time can you please check whats wrong in my UserTest) Many Thanks

    • You need to extend the TestCase class and load the setUp() method to resolve classes from the IoC container.

      • Nirmal Thapa

        Thanks for the hint. It worked now

        All I did was,
        class UserTest extends TestCase{}

        instead of,
        class UserTest extends PHPUnit_Framework_TestCase{}

        But the thing is I didnt resolve the classes from the IoC container (neither I remember doing it), it simply worked just by extending TestCase only.

        Anyway it worked. Thanks

        • Yeah because TestCase has a setUp method that loads the Laravel for you.

  • Wall05

    Hi

    This tutorial didn’t work at all for me. When I run PHPUnit it states that it cannot find ../../tests/models/factories.

    Any ideas why?

    • Did you create the fixtures file like this? https://github.com/thephpleague/factory-muffin#real-examples

      • Wall05

        Hi, thank you for the reply. I managed to get the test to fire by getting rid of some redundant files and starting again.

        However! When I run the test, I get General Error 1: No such table ‘users’.

        This is curious as I do indeed have a table called users!!!

  • 尤川豪

    I met some problems with functional tests recently. And I found this article today. Okay, so every time I met trouble, I found your articles. Thanks!

    • Haha, glad to hear you are finding my articles useful! :)