phpunit-testingphpunit

How to unit test a database insert function PDO


In the code below, how should I write unit tests? How should I mock the database? What's the point in mocking? Imagine the in the same save class we have a setDb method which is inject database dependency.

namespace Samtt\src; 
use \Resque;
class MoHandler {


    public $db; 
    public $resque; 
    private function getAuthToken($input){
        $arg = json_encode($input);
        return `./registermo $arg`;
    }

    public function setDatabase(Database $db) {
        $this->db = $db; 
    }

    public function setResque($resque) {
        $this->resque = $resque;
    }

    public function save($stackContents){

        if( !is_array($stackContent) || count($stackContents) == 0 )
            return false; 

        $this->db->query("INSERT INTO 
            `mo` (msisdn, operatorid, shortcodeid, text, auth_token, created_at)
             VALUES (:msisdn, :operatorid, :shortcodeid, :text , :auth_token, NOW())"
        );
        $this->db->beginTransaction();
        foreach($stackContents as $item) {

            $auth_token = $this->getAuthToken($stackContents);
            $this->db->bind(':msisdn', $item['msisdn']); 
            $this->db->bind(':operatorid', $item['operatorid']); 
            $this->db->bind(':shortcodeid', $item['shortcodeid']); 
            $this->db->bind(':text', $item['text']); 
            $this->db->bind(':auth_token', $auth_token); 
            $this->db->execute();
        }
        $this->db->endTransaction();

        echo 'A new job stack has been inserted into database ' . PHP_EOL ; 
    }


    /**
     * creating an extra queue in redis is much more efficent than 
     * inserting into mysql for every single request 
     * this function pack data and use batch insert to minimize 
     * database connection
     *  
     * @param  [array] $queueContents [description]
     * @return [boolean] 
     */
    public function createJobStack($input, $queue = null){

        if(! is_array($input)) {
            throw new \InvalidArgumentException;
        }

        // create job stack in redis with "current queue name" 
        // to avoid name collision with possible future job stack

        $stack = $queue .'-jobstack';
        $size  = $this->getSize($stack);
        if($size < Config::$minQueueSize) {

            // check incoming MO and if they are buggy 
            // don't let them pass !
            if(! Validator::validate($input, Config::$validKeys)) {
                echo 'buggy MO' . PHP_EOL ; 
                return false; 
            }

            $this->resque->push($stack, $input);
            echo 'new item has been added to ' . $stack . PHP_EOL; 
            echo 'current stack size is : ' . $size . PHP_EOL; 

        }else {

            $stackContents = [];
            for($i = 1; $i <= Config::$minQueueSize; $i++){
                array_push($stackContents, $this->resque->pop($stack));
            }

            // save the stack into database 
            $this->save($stackContents);
        }

        return true;

    }


    public function getSize($stack) {
        return $this->resque->size($stack);
    }


    public function setUp() {

        $this->setDatabase((new Database));
        $this->setResque((new Resque));

    }
    public function perform(){
        $this->createJobStack($this->args, $this->queue);
    }
}

i've edit and add the whole class . how many assertion do i need ?


Solution

  • Here a test case for check with wrong input data and a second test with correct data. In the second scenario we test the correct method invocation and an example of the bind method was called with the correct data.

    <?php
    
    
    namespace Samtt\src\Tests; 
    
    
    
    use Samtt\src\MoHandler;
    
    class MoHandlerTest extends \PHPUnit_Framework_TestCase {
    
        protected $database;
    
        protected $handler;
    
        public function setUp()
        {
            $this->database = $this->getMock('Samtt\src\Database',array('query','beginTransaction','bind','execute','endTransaction'));
            $this->handler = new MoHandler();
            $this->handler->setDatabase($this->database);
        }
    
        public function test_save_with_invalid_data()
        {
            $input = array();
    
            $this->database
                ->expects($this->never())
                ->method('query')
            ;
    
            $this->assertFalse($this->handler->save($input)); // Check te return value
        }
    
        public function test_save_with_correct_data()
        {
            $input = array(
                array(
                'msisdn' => 'msisdn',
                'operatorid' => 'operatorid',
                    'shortcodeid' =>'shortcodeid',
                    'text' =>'text'
                )
            );
    
            $this->database
                ->expects ($this->exactly(5))->method('bind')->withAnyParameters();
    
            $this->database
                ->expects($this->at(2))
                ->method('bind')
                ->with(':msisdn','msisdn')
            ;
    
            $this->database
                ->expects($this->once())
                ->method('query')
            ;
            $this->assertNull($this->handler->save($input)); // Check te return value
        }
    
    }
    

    Hope this help

    Further reference:

    http://www.andrejfarkas.com/2012/07/phpunit-at-method-to-check-method-invocation-at-certain-index/