testing persistence in php with dbunit

Post on 17-May-2015

4.056 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Testing persistence (in PHP)

PHPUnit & DbUnit

Our story

by unit testing classes

Started from inside to the outside

Tools and principles

Using PHPUnit with Mock objects to isolate the SUT

• Design for testability (SOLID)

• Use the front door first

• One condition per test

http://xunitpatterns.com/Principles of Test Automation.htmlhttp://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

PHPUnit

class TestClass     extends PHPUnit_Framework_TestCase{    public function setUp() {}    public function testMethod() {}    public function tearDown() {}}

Problems

No integration guarantees

Not effective for refactoring legacy code

Does not ensure external quality

Need for integration tests

To ensure external quality from top to bottom

Unit testing DAOs with DI and mocks

$connection = createMockExpecting(    " INSERT INTO table SET field = 'value' ");

$dao = new Dao($connection);$dao->insert('value');

Tells nothing about the database interaction

Testing DAOs - injected connection

$connection = createLocalTestConnection();

$dao = new Dao($connection);$dao->insert('value');

assertInsertedValueMatches('value');

Hard to replace in end to end tests

Dependency lookup

Testing DAOs - dependency lookup

$dao = new Dao();$dao->insert('value');

assertInsertedValueMatches('value');

http://xunitpatterns.com/Dependency Lookup.html

DbUnit + Etsy extensions

Test case initclass RemoverDaoTest extends DatabaseTestCaseBase{ /** * @return PHPUnit_Extensions_MultipleDatabase_Database[] */ protected function getDatabaseConfigs() { return array( $this->getDbConfigBuilder() ->connection($this->getCentralDbConnection()) ->dataSet($this->createXmlDataSet('init.xml')) ->build() ); }

Datasets

user: -   id: 1   name: Ingrid

<dataset>   <table name="user">       <column>id</column>       <column>name</column>       <row>           <value>1</value>           <value>Ingrid</value>       </row>   </table></dataset>

MySQL XML YAML

Assertions

public function testRemoveNoProblem(){   $remover = new RemoverDao();   $remover->removeUser(1);

   $this->assertDataSetsEqual( $this->createYamlDataSet('empty.yml'), $this->getCentralDbConnection() ->createDataSet(array('user'))   );}

Datasets

user: -   id: 1   name: Ingrid   created_at: 2012-01-16

<dataset>   <table name="user">       <column>id</column>       <column>name</column>       <column>created_at</column>       <row>           <value>1</value>           <value>Ingrid</value>           <value>2012-01-16</value>       </row>   </table></dataset>

MySQL XML YAML

Dataset filter

$datasetFilter = new PHPUnit_Extensions_Database_DataSet_DataSetFilter( $dataset);

$datasetFilter->addIncludeTables( array('user'));$datasetFilter->setExcludeColumnsForTable( 'user', array('created_at'));

Datasets

user: -   id: 1   name: Ingrid   created_at: ##TIME##

<dataset>   <table name="user">       <column>id</column>       <column>name</column>       <column>created_at</column>       <row>           <value>1</value>           <value>Ingrid</value>           <value>##TIME##</value>       </row>   </table></dataset>

MySQL XML YAML

Replacement dataset

$replacementDataset = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet(  $dataset, array('##TIME##' => TimeService::time()));

Implicit setup

Common initial state, extended test-by-test

Hard to share

Inline setup

• Define only the tables being used in common setup

• Insert all the necessary data inline in the test method

Still messy to share common datasets with xml or yml

Minimal fixture! 

Inline datasets with PHP

Value objects + builders

Convert to dataset for: • inserting into database• using in assertions

Setup

$user  = UserBuilder::create()->build();$video = VideoBuilder::create()  ->owner($user)  ->private()  ->build();

TestDataInserter::create(getCentralDbConnection())  ->add('user', $user)  ->add('video', $video);

Excercise

VideoPublisherDao::create()->publish($video->id);

Http service

DAO

HttpClient::create()->call('Video::publish', $video->id);

Verify

assertDataSetsEqual(  $this->createDataSet('video', $video),  $this->getCentralDbConnection() ->createDataSet(array('video')));

Layer crossing tests - where we use it

• Http services

• Daemons

• Cron jobs

• Ajax calls

The power of layer crossing tests

Refactoring (legacy) code

Increasing code coverage

There are downsides too

We use the back door => risk of overspecification

Empty initial state • hides concurrency problems• parallel testing more difficult

Concurrent end-to-end black box tests

Uses only the front door (public api)

Isolation with unique identifiers & Δ assertions

Tests real concurrency

Harder to track bugs & intermittent testshttp://engineering.imvu.com/2011/01/19/buildbot-and-intermittent-tests/

pepov@ustream.tvtwitter.com/pepov

top related