chriskite / phactory

A Database Factory for PHP Unit Tests
http://phactory.org
MIT License
140 stars 39 forks source link

Feature namespaces #19

Closed ramsey closed 12 years ago

ramsey commented 12 years ago

NOTE: This was originally submitted as Pull Request #17. I am submitting it as a new pull request in order to direct it into the next branch, rather than master.

WARNING: Accepting this pull request will break backwards compatibility with all previous versions of Phactory, as well as what's currently in master. I am submitting this for review and discussion more than blind acceptance into the Phactory master branch. Following discussion, you may feel that these modifications aren't good for the Phactory project or that they are best-suited in a Phactory2 repository, creating a logical split from the current version of the project. That's up to you.

This pull request introduces namespaces to Phactory. It also removes all static calls and singleton behavior, so that instances of Phactory objects must be created and passed around (dependency injection). These changes were made to solve the following problems I encountered:

As a result of these changes, backwards compatibility is broken with current master and all previous tags of Phactory. However, what is gained is:

Here is an example unit test using MongoDB and these updates to Phactory. This unit test will run as is, provided you have a local MongoDB database with a "testdb" database and a user with username "myuser" and password "mypass". It assumes you have this test in a file in the root of your project and that you have run composer.phar install so that there is a vendor/ directory with your autoloader in it. Feel free to give it a try.

<?php
namespace My\Foo;

require_once 'vendor/autoload.php';

use Phactory\Mongo\Phactory as PhactoryMongo;

class FooTestCase extends \PHPUnit_Framework_TestCase
{
    protected $mongo;
    protected $phactory;
    protected $fixtures = array();

    protected function setUp()
    {
        $this->mongo = new \Mongo('mongodb://localhost:27017', array(
            'db' => 'testdb',
            'username' => 'myuser',
            'password' => 'mypass',
        ));

        $this->phactory = new PhactoryMongo($this->mongo->testdb);
        $this->phactory->reset();

        $this->defineFixtures();
        $this->buildFixtures();
    }

    protected function tearDown()
    {
        $this->phactory->recall();
    }

    protected function defineFixtures()
    {
        $this->phactory->define('book', array(
            '_id' => null,
            'name' => 'The Great Book $n',
            'created' => new \MongoDate,
        ));

        $this->phactory->define('author', array(
            '_id' => null,
            'name' => 'J.R.R. Authorname $n',
            'description' => 'A widely respected test author.',
            'created' => new \MongoDate,
        ), array(
            'books' => $this->phactory->embedsMany('books'),
        ));

        $this->phactory->define('publisher', array(
            '_id' => null,
            'name' => 'Acme Publishing $n, LLC',
            'created' => new \MongoDate,
        ), array(
            'authors' => $this->phactory->embedsMany('authors'),
        ));
    }

    protected function buildFixtures()
    {
        // Books for Publisher 1, Author 1 (embedded doc, use "build")
        $book1 = $this->phactory->build('book', array('_id' => new \MongoId));
        $book2 = $this->phactory->build('book', array('_id' => new \MongoId));

        // Books for Publisher 1, Author 2 (embedded doc, use "build")
        $book3 = $this->phactory->build('book', array('_id' => new \MongoId));
        $book4 = $this->phactory->build('book', array('_id' => new \MongoId));

        // Books for Publisher 2, Author 3 (embedded doc, use "build")
        $book5 = $this->phactory->build('book', array('_id' => new \MongoId));
        $book6 = $this->phactory->build('book', array('_id' => new \MongoId));

        // Publisher 1, Author 1
        // (embedded doc with embedded items, use "buildWithAssociations")
        $author1 = $this->phactory->buildWithAssociations('author', array(
            'books' => array($book1, $book2),
        ), array(
            '_id' => new \MongoId,
        ));

        // Publisher 1, Author 2
        // (embedded doc with embedded items, use "buildWithAssociations")
        $author2 = $this->phactory->buildWithAssociations('author', array(
            'books' => array($book3, $book4),
        ), array(
            '_id' => new \MongoId,
        ));

        // Publisher 2, Author 3
        // (embedded doc with embedded items, use "buildWithAssociations")
        $author3 = $this->phactory->buildWithAssociations('author', array(
            'books' => array($book5, $book6),
        ), array(
            '_id' => new \MongoId,
        ));

        // Publisher 1
        // (parent doc with embedded items, use "createWithAssociations")
        $publisher1 = $this->phactory->createWithAssociations('publisher', array(
            'authors' => array($author1, $author2),
        ), array(
            '_id' => new \MongoId,
        ));

        // Publisher 2
        // (parent doc with embedded items, use "createWithAssociations")
        $publisher2 = $this->phactory->createWithAssociations('publisher', array(
            'authors' => array($author3),
        ), array(
            '_id' => new \MongoId,
        ));

        // Set the fixtures so that we may access them from the tests
        $this->fixtures['publisher1'] = $publisher1;
        $this->fixtures['publisher2'] = $publisher2;
    }

    public function testSomething()
    {
        $publisher = $this->mongo->testdb->publishers->findOne(array(
            '_id' => $this->fixtures['publisher1']['_id'],
        ));
        $this->assertEquals($this->fixtures['publisher1']['name'], $publisher['name']);
    }
}

This test will create structures in Mongo DB that look like this:

For Publisher 1:

{
    "_id" : ObjectId("4fe4bbf3dfb5cde968000009"),
    "name" : "Acme Publishing 0, LLC",
    "created" : ISODate("2012-06-22T18:39:47.122Z"),
    "authors" : [
        {
            "_id" : ObjectId("4fe4bbf3dfb5cde968000006"),
            "name" : "J.R.R. Authorname 0",
            "description" : "A widely respected test author.",
            "created" : ISODate("2012-06-22T18:39:47.121Z"),
            "books" : [
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000000"),
                    "name" : "The Great Book 0",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                },
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000001"),
                    "name" : "The Great Book 1",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                }
            ]
        },
        {
            "_id" : ObjectId("4fe4bbf3dfb5cde968000007"),
            "name" : "J.R.R. Authorname 1",
            "description" : "A widely respected test author.",
            "created" : ISODate("2012-06-22T18:39:47.121Z"),
            "books" : [
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000002"),
                    "name" : "The Great Book 2",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                },
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000003"),
                    "name" : "The Great Book 3",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                }
            ]
        }
    ]
}

For Publisher 2:

{
    "_id" : ObjectId("4fe4bbf3dfb5cde96800000a"),
    "name" : "Acme Publishing 1, LLC",
    "created" : ISODate("2012-06-22T18:39:47.122Z"),
    "authors" : [
        {
            "_id" : ObjectId("4fe4bbf3dfb5cde968000008"),
            "name" : "J.R.R. Authorname 2",
            "description" : "A widely respected test author.",
            "created" : ISODate("2012-06-22T18:39:47.121Z"),
            "books" : [
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000004"),
                    "name" : "The Great Book 4",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                },
                {
                    "_id" : ObjectId("4fe4bbf3dfb5cde968000005"),
                    "name" : "The Great Book 5",
                    "created" : ISODate("2012-06-22T18:39:47.121Z")
                }
            ]
        }
    ]
}