Модульное тестирование класса PHP, который принимает строковые аргументы для создания объекта PDO - PullRequest
0 голосов
/ 06 июля 2018

У меня есть класс (PDOMySQLForeignKeysToURLsService), который делает именно то, что говорит на жестяной коробке, он принимает объект базы данных в кодировке JSON, сравнивает его с файлом конфигурации и преобразует любые поля внешнего ключа в URL-адреса, ссылающиеся на другие ресурсы RESTful. т.е.

[
  {
    "ID": "1",
    "person_id": "1",
    "pc_name": "my_computer"
  }
]

при условии, что person_id является внешним ключом в базе данных, станет что-то вроде

[
  {
    "ID": "1",
    "person_id": "http://localhost/api/db/person/1",
    "pc_name": "my_computer"
  }
]

, поскольку сама служба инициализируется из файла конфигурации, она может принимать только текст в своем конструкторе,

public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname)

кажется, что все работает нормально, но сейчас я хочу написать несколько модульных тестов для этого класса, и часть из них требует создания экземпляра объекта PDO и запроса к таблице INFORMATION_SCHEMA, как показано ниже

  SELECT 
    *
  FROM
    INFORMATION_SCHEMA.KEY_COLUMN_USAGE
  WHERE
    TABLE_SCHEMA = ? AND
    CONSTRAINT_NAME != "PRIMARY" AND
    REFERENCED_TABLE_NAME IS NOT NULL AND
    REFERENCED_COLUMN_NAME IS NOT NULL

Метод, в котором это происходит, заключается в реализации интерфейса Resolvable, определяющего function resolveRequest(ServiceRequest $request, ServiceResponse $response)

Я, в принципе, понятия не имею, как обойти все это, кроме настройки схемы / некоторых таблиц в настройках тестов? Я слышал от своих коллег, что это плохая практика, и она не представляет собой модульное тестирование, а скорее интеграционное тестирование - поэтому мой вопрос состоит в том, как я могу провести модульное тестирование этого метода, уместно ли здесь модульное тестирование? Класс по определению очень зависит от MySQL (и PDO). Должен ли я написать какой-то адаптер вокруг PDO и использовать инфраструктуру DI для его внедрения и взаимодействовать с ним, а не напрямую с PDO? Я довольно растерян в том, что делать, и не понимаю, как и как смоделировать зависимости, которые используются только в рамках метода

1 Ответ

0 голосов
/ 11 июля 2018

Даже если вы не можете внедрить экземпляр PDO, все еще есть способы для модульного тестирования - это просто требует немного творческого подхода. Вероятно, самый простой способ модульного тестирования, который вы описываете, - это создание тестируемого подкласса, который допускает инъекцию. Точные детали будут зависеть от того, как реализован ваш класс, но простой пример может выглядеть примерно так:

class PDOMySQLForeignKeysToURLsService {
    protected $pdo;

    public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname) {
        $this->pdo = $this->createPdo(...);
    }

    // Encapsulate the PDO creation so that the subclass can override it.
    protected function createPdo(...) {
        return new PDO(...);
    }

    // Other methods omitted...
}

class TestablePDOMySQLForeignKeysToURLsService extends PDOMySQLForeignKeysToURLsService {
    // Override the constructor so you can pass in a mock PDO as the last parameter
    public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname, PDO $pdo) {
        // Save the injected mock PDO instance.
        $this->pdo = $pdo;
        parent::__construct(...);
    }

    // Override the creation so that you can just use the injected mock.
    protected function createPdo(...) {
        return $this->pdo;
    }
}

Класс TestablePDOMySQLForeignKeysToURLsService будет жить рядом с вашими тестами, и вы будете использовать его в своих тестовых случаях вместо производственного класса. Идея состоит в том, что класс «тестируемый» является минимальным расширением производственного класса - достаточно , достаточное для того, чтобы вы могли ввести двойные значения теста. Это позволяет вам проверить логику из производственного класса и смоделировать вызовы базы данных в вашем тесте, не меняя интерфейс на производственный класс. Это своего рода обман, но он выполняет свою работу.

...