1

i have a phpunit question regarding dbunit and how to keep data created in the database by one test for use in the next. i'm new to phpunit (we've been using an in-house tester for years but are finally trying to get with the modern age), so i apologize if this is a trivial issue.

the desired effect i have a mysql table that contains a column that is a unique key. if an attempt is made to insert a duplicate of this column, special things happen that i would like to be able to test. i have written a test to insert a value into this column (and test its success) and then written another test immediately afterwards to test how the class fails on attempting a duplicate value. i'd like to be able to catch that exception and test it. i am using dbunit to pre-fill my db with all the pre-filly stuff i need.

the problem at the commencement of each test it appears as if getDataSet() is called and, as a result, the unique key data i insert in the first test is no longer there to test against. consequently, i can't test the anticipated failure of inserting duplicate unique keys.

what i'm looking for well, obviously some way to persist the database data across tests; avoid calling getDataSet(), perhaps, at the beginning of the second test.

i certainly hope this is possible. i can't imagine why it wouldn't be; it seems like people should want to test duplicate insert! i am willing to entertain other solutions if they accomplish the task.

thanks in advance!

here's my test, stripped down to the relevant bits.

<?php
class UserPOSTTest extends \PHPUnit_Extensions_Database_TestCase
{

    static private $pdo = null;
    private $conn = null;

    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    public function getConnection()
    {
        if($this->conn === null) {
            if (self::$pdo == null) {
                self::$pdo = new \PDO('mysql:host=localhost;dbname=thedatabase', 'user', '*********');
            }
            $this->conn = $this->createDefaultDBConnection(self::$pdo, "db");
        }
        return $this->conn;
    }

    /**
     * @return PHPUnit_Extensions_Database_DataSet_IDataSet
     */
    public function getDataSet()
    {
        // this is returned at the beginning of every test
        return $this->createFlatXmlDataSet(dirname(__FILE__) . '/some_data_set.xml');
    }

    /**
     * test the insertion of the value "unique key value" into a column set as UNIQUE KEY in mysql
     * since getDataSet() has cleared this table, it passes.
     */
    public function uniqueKeyTest_passes() 
    {
        $inserter = new Inserter("unique key value");

        $this->assertEquals($inserter->one,1); // just some bogus assertion 

    } // uniqueKeyTest_passes

    /**
     * run the exact same insert as in uniqueKeyTest_passes() above. the purpose of this test is to
     * confirm how the Inserter class fails on the attempt to insert duplicate data into a UNIQUE KEY column.
     * however, the data inserted in uniqueKeyTest_passes() has been scrubbed out by getDataSet()
     * this is the crux of my question
     */
    public function uniqueKeyTest_should_fail() 
    {
        try {
            // exact same insert as above, should fail as duplicate
            $inserter = new Inserter("unique key value");
        }
        catch(Exception $e) {
            // if an exception is thrown, that's a pass
            return;
        }

        // the insert succeeds when it should not
        $this->fail("there should be an exception for attempting insert of unique key value here");

    } // uniqueKeyTest_should_fail 

}
3
  • You are about to do it wrong! :) It is a feature and a design goal that each test runs independently from others. Commented Aug 1, 2015 at 5:33
  • but, certainly, it is desirable to test functionality that relies on previous data. if you wish to test an update, an update of what? is there a "right" way to test my requirements? certainly there must be. for instance, i'm quite certain that people who write, say, a registration function will want to confirm that subsequent registrations don't attempt to duplicate, say, an email field? Commented Aug 1, 2015 at 5:47
  • Simply do all actions in the same test, insert, then update. You may utilize helper methods, which can be used in multiple test. Or even use a fixture, meaning a pre-initialized database. Check my answer to the duplicate key problem. Commented Aug 1, 2015 at 5:49

1 Answer 1

1

The fact that each test runs independently from others is a feature and a design goal of unit testing.

In your case you can simply use this:

/**
 * Confirm how the Inserter class fails on the attempt to 
 * insert duplicate data into a UNIQUE KEY column.
 * 
 * @expectedException Exception
 */
public function uniqueKeyTest_should_fail() 
{
    $inserter = new Inserter("unique key value");
    // exact same insert as above, should fail as duplicate
    $inserter = new Inserter("unique key value");
}

Please note the usage of @expectedException which simplifies the test code a lot. However, I would write my code in a way that it throws a DuplicateKeyException, this would make the test more specific. In the current form, you would for example not being able to detect a database connection error anymore (and the test would succeed!).

Sign up to request clarification or add additional context in comments.

1 Comment

well, i've written a 400 line test of nested try/catch/finally blocks using this approach (my example was greatly stripped down to just show the core problem)... but it works!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.