database - PHP Unit-Testing a function -


im trying test function, ive been trying difference ways not succeeding. have ide how can test in other way or maybe tell me whats wrong testing class(my testing class @ end of page).

function checkbrute($user_id, $mysqli) {     // timestamp of current time    $now = time();    // login attempts counted past 2 hours.     $valid_attempts = $now - (2 * 60 * 60);      if ($stmt = $mysqli->prepare("select time login_attempts user_id = ? , time > '$valid_attempts'")) {        $stmt->bind_param('i', $user_id);        // execute prepared query.       $stmt->execute();       $stmt->store_result();       // if there has been more 5 failed logins       if($stmt->num_rows > 5) {          return true;       } else {          return false;       }  } } 

here testing class, im connected database. , im trying function "testcheckbrute()" put value 16 id number , trying function.

<?php   include 'functions.php';   class test extends phpunit_extensions_database_testcase {  function getconnection(){  $mysqli = new mysqli('xxxxx.xxx.xx.se', 'xxx_xxxxxxxx', 'xxxxxx', 'db_xxxxxxxx');  if($mysqli->connect_errno > 0){     die('unable connect database [' . $mysqli->connect_error . ']');     } }  function testcheckbrute(){  $mysqli = new mysqli('atlas.dsv.su.se', 'xxx_xxxxxxx8', 'xxxxx', 'xx_xxxxxx');  checkbrute(16, $mysqli);  } function setup(){  } function getdataset(){  }}   ?> 

first of all, test case provided not unit test, it's called integration test, because depends on mysql server available in environment.

we'll doing integration testing, then. not delving in intricacies of proper db testing phpunit keep things simple enough, here's example test case class, written usability in mind:

tests.php

<?php require_once(__dir__.'/code.php'); class bruteforcetests extends phpunit_framework_testcase  {      /** @test */     public function nologinattemptsnobruteforce()     {         // given empty dataset random time         $any_random_time = date('h:i');          $this->assertfalse(             $this->isusertriedtobruteforce($any_random_time)         );     }      /** @test */     public function donotdetectbruteforceiflessthanfiveloginattemptsinlasttwohours()     {         $this->userlogged('5:34');         $this->userlogged('4:05');          $this->assertfalse(             $this->isusertriedtobruteforce('6:00')         );     }      /** @test */     public function detectbruteforceifmorethanfiveloginattemptsinlasttwohours()     {         $this->userlogged('4:36');         $this->userlogged('4:23');         $this->userlogged('4:00');         $this->userlogged('3:40');         $this->userlogged('3:15');         $this->userlogged('3:01'); // ping! 6th login, in time          $this->asserttrue(             $this->isusertriedtobruteforce('5:00')         );     }      //==================================================================== setup      /** @var pdo */     private $connection;      /** @var pdostatement */     private $inserter;      const dbname = 'test';     const dbuser = 'tester';     const dbpass = 'secret';     const dbhost = 'localhost';      public function setup()     {         $this->connection = new pdo(             sprintf('mysql:host=%s;dbname=%s', self::dbhost, self::dbname),              self::dbuser,              self::dbpass         );         $this->assertinstanceof('pdo', $this->connection);          // cleaning after possible previous launch         $this->connection->exec('delete login_attempts');          // caching insert statement perfomance         $this->inserter = $this->connection->prepare(             'insert login_attempts (`user_id`, `time`) values(:user_id, :timestamp)'         );         $this->assertinstanceof('pdostatement', $this->inserter);     }      //================================================================= fixtures      // user id of user care     const user_under_test = 1;     // user id of user noise in db, , should skipped tests     const some_other_user = 2;      /**      * use method record login attempts of user care      *       * @param string $datetime date & time definition `strtotime()` understands.      */      private function userlogged($datetime)     {         $this->loguserlogin(self::user_under_test, $datetime);     }      /**      * use method record login attempts of user not care about,      * provide fuzziness our tests      *      * @param string $datetime date & time definition `strtotime()` understands.      */      private function anotheruserlogged($datetime)     {         $this->loguserlogin(self::some_other_user, $datetime);     }      /**      * @param int $userid      * @param string $datetime human-readable representation of login time (and possibly date)      */     private function loguserlogin($userid, $datetime)     {         $mysql_timestamp = date('y-m-d h:i:s', strtotime($datetime));         $this->inserter->execute(             array(                 ':user_id' => $userid,                 ':timestamp' => $mysql_timestamp             )         );         $this->inserter->closecursor();     }      //=================================================================== helpers      /**      * helper imitate calling of our function under test       * id of user care about, clean connection of correct type , provided testing datetime.      * can call helper human-readable datetime value, although function under test      * expects integer timestamp origin date.      *       * @param string $datetime human-readable datetime value      * @return bool value of called function under test.      */     private function isusertriedtobruteforce($datetime)     {         $connection = $this->trygetmysqliconnection();         $timestamp = strtotime($datetime);         return wastryingtobruteforce(self::user_under_test, $connection, $timestamp);     }      private function trygetmysqliconnection()     {         $connection = new mysqli(self::dbhost, self::dbuser, self::dbpass, self::dbname);         $this->assertsame(0, $connection->connect_errno);         $this->assertequals("", $connection->connect_error);         return $connection;     }  } 

this test suite self-contained , has 3 test cases: when there's no records of login attempts, when there's 6 records of login attempts within 2 hours of time of check , when there's 2 login attempt records in same timeframe.

this insufficient test suite, example, need test check bruteforce works user interested , ignores login attempts of other users. example function should select records inside 2 hour interval ending in time of check, , not records stored after time of check minus 2 hours (as now). can write remaining tests yourself.

this test suite connects db pdo, absolutely superior mysqli interface, needs of function under test creates appropriate connection object.

a important note should taken: function is untestable because of static dependency on uncontrollable library function here:

// timestamp of current time $now = time(); 

the time of check should extracted function argument function testable automatic means, so:

function wastryingtobruteforce($user_id, $connection, $now) {     if (!$now)         $now = time();     //... rest of code ... } 

as can see, have renamed function more clear name.

other that, suppose should careful when working datetime values in between mysql , php, , never ever construct sql queries concatenating strings, using parameter binding instead. so, cleaned version of initial code follows (note test suite requires in first line):

code.php

<?php  /**  * checks whether user trying bruteforce login.  * bruteforce defined 6 or more login attempts in last 2 hours $now.  * default $now current time.  *   * @param int $user_id id of user in db  * @param mysqli $connection result of calling `new mysqli`  * @param timestamp $now base timestamp count 2 hours  * @return bool whether $user_id tried bruteforce login or not.  */ function wastryingtobruteforce($user_id, $connection, $now) {     if (!$now)         $now = time();      $two_hours_ago = $now - (2 * 60 * 60);     $since = date('y-m-d h:i:s', $two_hours_ago); // checking records of login attempts last 2 hours      $stmt = $connection->prepare("select time login_attempts user_id = ? , time > ?");      if ($stmt) {          $stmt->bind_param('is', $user_id, $since);          // execute prepared query.         $stmt->execute();         $stmt->store_result();         // if there has been more 5 failed logins         if ($stmt->num_rows > 5) {             return true;         } else {             return false;         }     } } 

for personal tastes, method of checking quite inefficient, want make following query:

select count(time)      login_attempts               user_id=:user_id          , time between :two_hours_ago , :now 

as integration test, expects working accessible mysql instance database in , following table defined:

mysql> describe login_attempts; +---------+------------------+------+-----+-------------------+----------------+ | field   | type             | null | key | default           |          | +---------+------------------+------+-----+-------------------+----------------+ | id      | int(10) unsigned | no   | pri | null              | auto_increment | | user_id | int(10) unsigned | yes  |     | null              |                | | time    | timestamp        | no   |     | current_timestamp |                | +---------+------------------+------+-----+-------------------+----------------+ 3 rows in set (0.00 sec) 

it's personal guess given workings of function under test, suppose have table that.

before running tests, have configure db* constants in "setup" section within tests.php file.


Comments

Popular posts from this blog

jquery - How can I dynamically add a browser tab? -

node.js - Getting the socket id,user id pair of a logged in user(s) -

keyboard - C++ GetAsyncKeyState alternative -