Phactory

Post on 17-May-2015

1.347 views 0 download

Tags:

Transcript of Phactory

© 2009 Vertive, Inc.

Proprietary and Confidential

A Database Factory for PHP

http://phactory.org

Chris Kite

Sr. Web Engineer at Offers.com

@chriskite

© 2009 Vertive, Inc.

Proprietary and Confidential

Who are we?

Chris Kite

» B.S. in Computer Science from UT Austin

» Senior Web Engineer at Offers.com

» Creator of Phactory

» Likes martial arts, sushi

Josh Butts

» B.A. in Computer Science from UT Austin

» Director of Web Development at Offers.com

» Likes fast cars, plain burgers

© 2009 Vertive, Inc.

Proprietary and Confidential

Phactory: What is it?

Alternative to database fixtures for unit

tests

Define and create objects in code

Provides a lightweight ORM

Works with MySQL, SQLite, and

MongoDB

Phactory is developed and used at

Offers.com

© 2009 Vertive, Inc.

Proprietary and Confidential

Databases in Unit Testing

© 2009 Vertive, Inc.

Proprietary and Confidential

Database Fixtures

A fixture is a statically defined set of data

Each test can have its own dataset

PHPUnit provides support for loading

fixtures:class DatabaseTest extends PHPUnit_Extensions_Database_TestCase{

protected function getConnection(){

$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', '');return $this->createDefaultDBConnection($pdo, 'testdb');

}

protected function getDataSet(){

return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/bank-account-seed.xml');}

}

© 2009 Vertive, Inc.

Proprietary and Confidential

Database Fixtures

<?xml version="1.0" encoding="UTF-8" ?>

<dataset>

<post

post_id="1"

title="My First Post"

date_created="2008-12-01 12:30:29"

contents="This is my first post" rating="5"

/>

<post

post_id="2"

title="My Second Post"

date_created="2008-12-04 15:35:25"

contents="This is my second post"

/>

</dataset>

Example from http://www.phpunit.de/manual/current/en/database.html

© 2009 Vertive, Inc.

Proprietary and Confidential

Database Fixtures

Drawbacks:

» Typically in an unfriendly format like XML

» You are left to your own devices to retrieve

and manipulate data in your test

» Can’t dynamically create objects

» No concept of associations

© 2009 Vertive, Inc.

Proprietary and Confidential

Database Factories

Define database test data in code, not flat

files

Create objects dynamically, rather than

loading them all at once

Define associations between objects

Can integrate with or provide ORM

functionality

© 2009 Vertive, Inc.

Proprietary and Confidential

Database Factories

<? Phactory::setConnection(new PDO('sqlite:test.db')); Phactory::define('user', array('name' => 'Test User', 'email' => 'user@example.com')); $user = Phactory::create('user'); // creates a row in the 'users' table print("Hello, {$user->name}!"); // prints "Hello, Test User!"

Phactory Example

© 2009 Vertive, Inc.

Proprietary and Confidential

Using Phactory

© 2009 Vertive, Inc.

Proprietary and Confidential

Connecting to the Database

Phactory supports MySQL, SQLite, and

MongoDB

Uses PDO for SQL databases

<? require_once 'Phactory/lib/Phactory.php'; $pdo = new PDO('mysql:host=127.0.0.1; dbname=testdb', 'user', 'password'); Phactory::setConnection($pdo);

© 2009 Vertive, Inc.

Proprietary and Confidential

Defining Table Blueprints

A blueprint defines default values for

objects created in a particular table

<? Phactory::define('user', array('name' => 'test_user', 'age' => 20)); Phactory::define('post', array('text' => 'This is a post', 'created_at' => 1296663323));

© 2009 Vertive, Inc.

Proprietary and Confidential

Creating Objects

When creating an object with Phactory,

you can:

» Specify values for fields that weren’t in the

blueprint definition

» Override any of the default values

» Associate with other objects

© 2009 Vertive, Inc.

Proprietary and Confidential

Creating Objects

Phactory::define('user', array('name' => 'test_user', 'age' => 20)); $john = Phactory::create('user', array('name' => 'John Doe')); $joe = Phactory::create('user', array('name' => 'Joe Smith', 'activated' => 1)); $anon = Phactory::create('user'); // $john looks like this: $john->id == 1; $john->name == 'John Doe'; $john->activated == 0; // $joe looks like this: $joe->id == 2; $joe->name == 'Joe Smith'; $joe->activated == 1; // $anon looks like this: $anon->id == 3; $anon->name == 'test_user'; $anon->activated == 0;

© 2009 Vertive, Inc.

Proprietary and Confidential

Retrieving Objects

Basic ORM functionality allows you to

retrieve Phactory objects from the

database

Uses a simple get() method which takes

an array of columns to match

$joe = Phactory::get('user', array('id' => 2)); $joe = Phactory::get('user', array('age' => 20, 'activated' => 1));

© 2009 Vertive, Inc.

Proprietary and Confidential

Associations

For SQL databases, Phactory provides

many-to-one and many-to-many

associations

Foreign key columns and join tables are

filled in automatically when creating an

object with associations

© 2009 Vertive, Inc.

Proprietary and Confidential

Associations

Phactory::define('author'); Phactory::define('book', array('name' => 'Test Book'), array('primary_author' => Phactory::manyToOne('author'))); $twain = Phactory::create('author', array('name' => 'Mark Twain')); $book = Phactory::createWithAssociations('book', array('primary_author' => $twain)); $book->author_id == $twain->getId();

© 2009 Vertive, Inc.

Proprietary and Confidential

Sequences

Define blueprints with automatically

incrementing values

» Include ‘$n’ in the definition

» Sequence starts at 0, and is incremented

each time an object is created

Phactory::define('user', array('name' => 'user$n', 'age' => '$n')); $user0 = Phactory::create('user'); // name == 'user0', age == '0' $user1 = Phactory::create('user'); // name == 'user1', age == '1' $user2 = Phactory::create('user'); // name == 'user2', age == '2'

© 2009 Vertive, Inc.

Proprietary and Confidential

Phactory and PHPUnit

© 2009 Vertive, Inc.

Proprietary and Confidential

Basic Test Setuprequire_once 'Phactory/lib/Phactory.php';

class ExampleTest extends PHPUnit_Framework_TestCase

{ public static function setUpBeforeClass()

{ // create a db connection and tell Phactory to use it

$pdo = new PDO("sqlite:test.db");

Phactory::setConnection($pdo);

// reset any existing blueprints and empty any tables Phactory has used Phactory::reset();

// define default values for each user we will create Phactory::define('user', array('name' => 'Test User $n', 'age' => 18));

}

public static function tearDownAfterClass() {

Phactory::reset();

}

public function tearDown() {

// delete all objects from defined tables, but do not erase blueprints

Phactory::recall(); }

© 2009 Vertive, Inc.

Proprietary and Confidential

Structuring Shared Definitions

Usually many of the test classes in your

suite will need to use the same tables

Define blueprints in one place, share them

among many tests

» Can include() a file of definitions, put

definitions in a static class, etc.

© 2009 Vertive, Inc.

Proprietary and Confidential

Structuring Shared Definitions

<? Phactory::define('tag', array('name' => 'Tag $n')); Phactory::define('post', array('name' => 'Test Blog Post', 'content' => 'Test Content'), array('tag' => Phactory::manyToMany('tag', 'posts_tags')));

SharedDefinitions.php

class ExampleTest extends PHPUnit_Framework_TestCase { public static function setUpBeforeClass() { $pdo = new PDO("sqlite:test.db"); Phactory::setConnection($pdo); Phactory::reset(); // include definitions common to several tests include('SharedDefinitions.php'); } }

ExampleTest.php

© 2009 Vertive, Inc.

Proprietary and Confidential

Dynamic Objects

Recall that with fixtures, your test data is

all loaded at the start of the test

With Phactory, you can create or change

test data during a test

© 2009 Vertive, Inc.

Proprietary and Confidential

Dynamic Objects

Phactory::define('tag', array('name' => 'Tag $n'), array('post' => Phactory::manyToMany('post', 'posts_tags'))); Phactory::define('post', array('name' => 'Test Blog Post', 'content' => 'Test Content'), array('tag' => Phactory::manyToMany('tag', 'posts_tags'))); $post = Phactory::create('post'); $this->assertEquals(0, MyPostClass::countTagsForPost($post->getId())); $tag = Phactory::createWithAssociations('tag', array('post' => $post)); $this->assertEquals(1, MyPostClass::countTagsForPost($post->getId()));

class MyPostClass { public static function countTagsForPost($post_id) { $stmt = $pdo->prepare("SELECT COUNT(*) AS num_tags FROM `posts_tags` WHERE `post_id` = ?"); $stmt->execute(array($post_id)); $result = $stmt->fetch(); return $result['num_tags']; } }

© 2009 Vertive, Inc.

Proprietary and Confidential

Using Phactory with MongoDB

require_once 'Phactory/lib/PhactoryMongo.php';

Uses the Mongo PHP driver

Almost exactly the same as regular SQL

Phactory, except:

» Documents returned as arrays rather than

Phactory_Row objects

» Phactory::get() supports all Mongo queries

» Associations: embedsOne and embedsMany

http://phactory.org/mongodb-guide/

© 2009 Vertive, Inc.

Proprietary and Confidential

QUESTIONS

© 2009 Vertive, Inc.

Proprietary and Confidential

We're Hiring!

Offers.com is hiring PHP Developers and

Software Engineers in Austin, TX

http://www.vertive.com/careers

© 2009 Vertive, Inc.

Proprietary and Confidential

Thank You!

These slides are online at

http://slidesha.re/phactory

Contact us on Twitter: @chriskite and

@jimbojsb

Visit http://phactory.org to download and

for more information