1

I'm still pretty new with PHPUnit, and I'm trying to figure out how to best test methods as shown in the 3rd class.

I understand how the mock databases work (I think), in that they can return values based on input, from an XML file, etc. I'm not sure how to provide data for the 3rd example, when SQL queries are run inside the methods itself.

I'm trying to test code that accesses info from a DB and performs operations on it. There is no way to feed mocked DB data (e.g., arrays) to these methods currently.

Question: What is the best way to provide data to a method that handles all of its SQL queries internally?

<?php
class ThisMakesSense {

    public function checkPassword($original, $hash) {
        return (md5($original) == $hash);
    }

}


class ThisMakesSenseTest {

    public function testCheckPassword() {
        $tms = new ThisMakesSense();
        $data = array('[email protected]' => 'password1', '[email protected]' => 'password2');        
        foraech ($data as $email => $password) {
            $hash = $this->mockDB()->doStuff()->getPasswordHashByEmail($email);
            $this->assertTrue($tms->checkPassword($password, $hash), "Password for {$email} is invalid");
        }

        $tms->checkPassword($password, $hash);
    }
}

/* The '$this->_db' object is basically an OOP way of using the
 * mysql_* /mysqli_* functions. Replacing it is not an option
 * right now.
 */
class DontUnderstand {
    public function checkPassword($email, $password) {
        $this->_db->query("SELECT password_hash FROM users WHERE email = '{$email}'");
        $row = $this->_db->fetchAssoc();
        return (md5($password) == $row['password_hash']);
    }
}


class DontUnderstandTest extends PHPUnit_Framework_TestCase {
    public function testCheckPassword() {
        $du = new DontUnderstand();
        $data = array('[email protected]' => 'password1', '[email protected]' => 'password2');

        foreach ($data as $email => $pass) {
            $this->assertTrue($du->checkPassword($email, $pass), "Password for {$email} is invalid");
        }
    }
}

(To save someone the trouble of commenting, the md5 and query methods are just for a simple example)

1 Answer 1

1

I'm not sure what is the best approach, but here's my way. It's based on the assumption of a class that connects internally with a database and a single table. Access is through INSERT, UPDATE, DELETE, SELECT and similar, so no complex JOINs or UNIONs. Another assumption is that I erect the database once (not as part of the testing routine) before I run phpunit. I looked at PHPUnit's Database extension but it appeared to me as too cumbersome to use here, so I quickly mocked this:

class UserProfileTest extends PHPUnit_Framework_TestCase {

  protected static
    $options,   
    $dbHandle;      

  public static function setUpBeforeClass() {
    self::$options = array(
               'dbHostName' => 'localhost',
               'dbUserName' => 'tester',
               'dbPassword' => 'pw4tester',
               'dbName' => 'test',
               'dbTableName' => 'Test',
               );            
    self::$dbHandle = new mysqli( self::$options[ 'dbHostName'], self::$options[ 'dbUserName'], self::$options[ 'dbPassword'], self::$options[ 'dbName'] );
    if( self::$dbHandle->connect_errno)
      exit( 'Error: No DB connection.');
  }

  protected function fillDb() {
    $query = 'INSERT INTO ' . self::$options[ 'dbTableName'] . ' VALUES (default,"foo","bar",7)';
    if( !self::$dbHandle->query( $query) )
      exit( 'Error: Could not fill DB.');
  }

  protected function setUp() {
    // always start a TC with empty DB
    $query = 'DELETE FROM ' . self::$options[ 'dbTableName'];
    if( !self::$dbHandle->query( $query) )
      exit( 'Error: Could not empty DB.');
  }

  // -- test --
  public function testGetNumberOfProfiles() {
    $profileMgr = new UserProfile( self::$options);
    $this->assertEquals( 0, $profileMgr->getNumberOfProfiles() );
    $this->fillDb();
    $this->assertEquals( 1, $profileMgr->getNumberOfProfiles() );
    $this->fillDb();
    $this->assertEquals( 2, $profileMgr->getNumberOfProfiles() );
  }

So, you connect to the DB when the class is instantiated (setUpBeforeClass), and empty the table before each testcase (in setUp). There is a helper function which inserts a row into the table; it is called when needed.

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

Comments

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.