2013-05-06 1 views
1

Может кто-нибудь, пожалуйста, помогите мне настроить единичный тест для этого PHP-кода, который пытался и пока не нашел решение. Я получил этот код от команды разработчиков, и я предположил создать модульные тесты для каких-либо идей о том, как это сделать?Как настроить тест для конкретной функции php

function checkbrute($user_id, $mysqli) { 
    // Get timestamp of current time 
    $now = time(); 
    // All login attempts are counted from the past 2 hours. 
    $valid_attempts = $now - (2 * 60 * 60); 

    if ($stmt = $mysqli->prepare("SELECT time FROM login_attempts WHERE user_id = ? AND time > '$valid_attempts'")) { `enter code here` 
     $stmt->bind_param('i', $user_id); 
     // Execute the prepared query. 
     $stmt->execute(); 
     $stmt->store_result(); 
     // If there has been more than 5 failed logins 
     if($stmt->num_rows > 5) { 
     return true; 
     } else { 
     return false; 
     } 
    } 
} 

ответ

-1

модульный тест прост для создания: он помещает данные в базе данных (со вставками), и проверяет, является ли возвращение функции является хорошим на этих данных. Затем, когда тест завершен, он удаляет данные.

Здесь необходимо вставить значения в login_attempts для Exemple:

INSERT login_attempts(user_id, time, ...) VALUES (12, 1367849298) 

, а затем проверить возвращаемое значение вашей функции.

+0

Где я могу запустить этот код? И знаете ли вы какие-либо хорошие учебники или веб-сайт, которые могут помочь мне изучить это. – user2354898

+0

Вы можете запускать этот код везде, где хотите! Проверьте эту ссылку: http://phpunit.de/manual/3.7/ru/writing-tests-for-phpunit.html. Я нашел его, выполнив поиск «php unit test» в Google ... Возможно, это поможет вам. – Alarid

+0

Знаете ли вы, как тестировать функции, не имеющие класса? Мне был предоставлен файл с очень многими функциями, которые я бы хотел выполнить с помощью unit-test, но я не могу заставить его работать без использования классов. Любые идеи, как решить это без реализации классов? – user2354898

0

Если тест касается базы данных (или сети, файловой системы или любой другой внешней службы), это не единичный тест. Исходя из любого определения, тестирование через БД происходит медленно и подвержено ошибкам. В динамических языках слишком малое издевательство, но многие люди все еще проверяют бизнес-логику через БД. Я обычно не поклонник публикации полных решений вопроса. В этом случае я чувствую необходимость сделать это, чтобы доказать, что на самом деле это довольно легко, особенно в постановке, описанной в вопросе.

class CheckBruteTest extends PHPUnit_Framework_TestCase { 
    public function test_checkbrute__some_user__calls_db_and_statement_with_correct_params() { 
     $expected_user_id = 23; 
     $statement_mock = $this->getMock('StatementIface', array()); 
     $statement_mock->expects($this->once())->method('bind_param') 
      ->with($this->equalTo('i'), $this->equalTo($expected_user_id)); 
     $statement_mock->expects($this->once())->method('execute'); 
     $statement_mock->expects($this->once())->method('store_result'); 

     $db_mock = $this->getMock('DbIface', array()); 
     $time_ignoring_the_last_two_decimals = floor((time() - 2 * 60 * 60)/100); 
     $db_mock->expects($this->once())->method('prepare') 
      ->with($this->stringStartsWith("SELECT time FROM login_attempts WHERE user_id = ? AND time > '$time_ignoring_the_last_two_decimals")) 
      ->will($this->returnValue($statement_mock)); 

     checkbrute($expected_user_id, $db_mock); 
    } 

    public function test_checkbrute__more_then_five__return_true() { 
     $statement_mock = $this->getMock('StatementIface', array()); 
     $statement_mock->num_rows = 6; 
     $db_mock = $this->getMock('DbIface', array()); 
     $db_mock->expects($this->once())->method('prepare') 
      ->will($this->returnValue($statement_mock)); 

     $result = checkbrute(1, $db_mock); 

     $this->assertTrue($result); 
    } 

    public function test_checkbrute__less_or_equal_then_five__return_false() { 
     $statement_mock = $this->getMock('StatementIface', array()); 
     $statement_mock->num_rows = 5; 
     $db_mock = $this->getMock('DbIface', array()); 
     $db_mock->expects($this->once())->method('prepare') 
      ->will($this->returnValue($statement_mock)); 

     $result = checkbrute(1, $db_mock); 

     $this->assertFalse($result); 
    } 
} 
interface DbIface { 
    public function prepare($query); 
} 
abstract class StatementIface { 
    public abstract function bind_param($i, $user_id); 
    public abstract function execute(); 
    public abstract function store_result(); 
    public $num_rows; 
} 

Поскольку я не знаю спецификации функции, я могу получить ее только из кода.

В первом тестовом случае я проверяю только mocks для DB и инструкцию, если код действительно вызывает эти службы, как ожидалось. Самое главное, что он проверяет, правильно ли вводится правильный идентификатор пользователя, и если используется правильное временное ограничение. Проверка времени довольно халатная, но это то, что вы получаете, если вы вызываете такие услуги, как time() непосредственно в код вашего бизнеса.

Второй тестовый случай заставляет количество попыток входа быть 6 и утверждает, возвращает ли функция true.

Третий тестовый случай заставляет количество попыток входа быть 5 и утверждает, возвращает ли функция false.

Это охватывает (почти) все пути кода в функции. Только один путь кода не учитывается: если $mysqli->prepare() возвращает значение null или любое другое значение, которое оценивается в false, весь if-блок обходит и неявно возвращает значение null. Я не знаю, если это специально. Код должен сделать что-то вроде этого явным.

Для издевательских соображений я создал небольшой интерфейс и небольшой абстрактный класс. Они нужны только в контексте тестов. Можно также реализовать пользовательские макетные классы для параметра $mysqli и возвращаемого значения $mysqli->prepare(), но я предпочитаю использовать автоматические mocks.

Некоторые дополнительные примечания, которые не имеют ничего общего с решением:

  • модульных тестов являются тесты для разработчиков и должны быть написаны сами разработчики, а не какой-то бедный тестером. Тестеры пишут приемные и регрессионные тесты.
  • «Взлом» тестовых примеров показывает, почему писать тесты после того, как факт намного сложнее. Если бы разработчики написали код TDD, код И тест были бы намного чище.
  • Конструкция функции checkbrute довольно субоптимальна:
    • «checkbrute» - это плохое имя. На самом деле это не так.
    • Он смешивает бизнес-код с доступом к базе данных. Расчет ограничения времени - это бизнес-код, а также проверка на наличие >5. Код между ними - это код DB и принадлежит его собственной функции/классу/независимо.
    • Магические номера. Пожалуйста, используйте константы для магических чисел, таких как 2h-значение и максимальное количество попыток входа в систему.