0

我想测试其中一位开发人员制作的登录功能。我想做的是在我的设置中创建测试用户,然后我想使用这些用户来测试登录功能。我希望看到当用户名和密码等于我们数据库中存储的值时它返回正确的布尔值。我的问题是,当我现在插入我的用户时,我以纯文本形式提供密码,因此它们以纯文本形式存储在数据库中,但是这里有一个问题。登录功能会在您尝试登录时对密码进行哈希处理,因为在注册完成后会对数据库中的密码进行哈希处理。所以基本上我刚刚插入的纯文本密码永远不会匹配,因为登录函数会散列密码以尝试找到匹配项。所以我需要做的是在我插入测试用户的密码时对其进行哈希处理。我该怎么做呢?这是我的代码目前的样子:

<?php

include 'functions.php';


class Test extends PHPUnit_Framework_TestCase {


protected function setUp(){

global $mysqli;
$mysqli = new mysqli('localhost', 'xxx', 'xxxxx', 'xxxxx');


$mysqli->query("INSERT INTO members (id, username, pnumber, password) VALUES  ('200',        'testbrute', '920314', 'xxxxx')");


}


public function testLogin(){

global $mysqli;

    $correctPass = Login('920314','xxxxxx', $mysqli);
    $this->assertTrue($correctPass);


    $wrongPass = Login('920314','xxxxxxxxx', $mysqli);
    $this->assertFalse($wrongPass);


$NoUserExists = Login("980611-5298","--..--..--", $mysqli);
$this->assertFalse($NoUserExists);

}


protected function tearDown(){
$mysqli = new mysqli('localhost', 'xxx', 'xxxxx', 'xxxxx'); 



$mysqli->query("DELETE FROM members WHERE id IN (200)");


}

}
?>

这是登录功能的样子:

function login($pnumber, $password, $mysqli) {
 // Using prepared Statements means that SQL injection is not possible. 
 if ($stmt = $mysqli->prepare("SELECT id, username, password, salt FROM members WHERE     pnumber = ? LIMIT 1")) { 
  $stmt->bind_param('s', $pnumber); // Bind "$pnumber" to parameter.
  $stmt->execute(); // Execute the prepared query.
  $stmt->store_result();
  $stmt->bind_result($user_id, $username, $db_password, $salt); // get variables from  result.
  $stmt->fetch();
  $password = hash('sha512', $password.$salt); // hash the password with the unique salt.

  if($stmt->num_rows == 1) { // If the user exists
     // We check if the account is locked from too many login attempts
     if(checkbrute($user_id, $mysqli) == true) { 
        // Account is locked
        // Send an email to user saying their account is locked
        return "account locked";
     } else {
     if($db_password == $password) { // Check if the password in the database matches the password the user submitted. 
        // Password is correct!

           $ip_address = $_SERVER['REMOTE_ADDR']; // Get the IP address of the user. 
           $user_browser = $_SERVER['HTTP_USER_AGENT']; // Get the user-agent string of the user.

           $user_id = preg_replace("/[^0-9]+/", "", $user_id); // XSS protection as we     might print this value
           $_SESSION['user_id'] = $user_id; 
           $username = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $username); // XSS     protection as we might print this value
           $_SESSION['username'] = $username;
           $_SESSION['login_string'] = hash('sha512',        $password.$ip_address.$user_browser);
           // Login successful.
           return "login successful";    
     } else {
        // Password is not correct
        // We record this attempt in the database
        $now = time();
        $mysqli->query("INSERT INTO login_attempts (user_id, time) VALUES ('$user_id',  '$now')");
        return 'pass incorrect';
     }
  }
  } else {
     // No user exists. 
     return 'no exist';
      }
   }
}

我是 phpunit 和一般测试的新手,所以请过度描述。

4

1 回答 1

0

忘记尝试针对实际数据库进行测试。作为一般规则,如果您能提供帮助,您不希望您的测试依赖于外部服务。

您可以注入一个模拟mysqli对象并指定它的行为。然后您不必担心将任何值添加到数据库中或依赖于数据库是否存在。

所以在你的测试中,而不是声明一个global $mysqli做:

$mockMysqli = $this->getMockBuilder('mysqli')
                   ->disableOriginalConstructor()
                   ->setMethods(array('prepare'))
                   ->getMock();

$mockMysqli->expects($this->once())
           ->method('prepare')
           ->will($this->returnValue($mockStmt) //Have to also create a mock mysqli_stmt object

根据你的函数是怎样的,你最终会得到一些返回其他模拟对象的模拟对象,这是你的函数做太多的代码味道。正因为如此,你最好把它分解成更小的部分,然后分别进行模拟和测试。我发现一般来说,如果功能很难测试,那么它不是一个好的设计,应该重构。大多数好的设计最终很容易用一两个模拟对象进行测试。

于 2013-07-11T14:24:03.073 回答