Как мне переписать мою базу данных execute / commit, чтобы сделать ее пригодной для модульного тестирования? - PullRequest
6 голосов
/ 20 апреля 2010

Я пытался начать модульное тестирование, работая над небольшой программой cli.

Моя программа в основном анализирует аргументы и параметры командной строки и решает, какую функцию вызывать. Каждая из функций выполняет некоторые операции с базой данных.

Так, например, у меня может быть функция create:

def create(self, opts, args):
    #I've left out the error handling.
    strtime = datetime.datetime.now().strftime("%D %H:%M")
    vals = (strtime, opts.message, opts.keywords, False)
    self.execute("insert into mytable values (?, ?, ?, ?)", vals)
    self.commit()

Должен ли мой тестовый пример вызывать эту функцию, а затем выполнить select sql, чтобы проверить, что строка была введена? Это звучит разумно, но также усложняет проведение тестов. Перепишите ли вы функцию, чтобы она возвращала что-то и проверяли возвращаемое значение?

Спасибо

Ответы [ 4 ]

8 голосов
/ 20 апреля 2010

Ответ Алекса охватывает подход внедрения зависимостей. Другим является фактор вашего метода. В его нынешнем виде он состоит из двух этапов: создание оператора SQL и выполнение оператора SQL. Вы не хотите тестировать второй этап: вы не написали движок SQL или базу данных, вы можете предположить, что они работают правильно. Этап 1 - ваша работа: создание оператора SQL. Таким образом, вы можете реорганизовать код так, чтобы вы могли протестировать только этап 1:

def create_sql(self, opts, args):
    #I've left out the error handling.
    strtime = datetime.datetime.now().strftime("%D %H:%M")
    vals = (strtime, opts.message, opts.keywords, False)
    return "insert into mytable values (?, ?, ?, ?)", vals

def create(self, opts, args):
    self.execute(*self.create_sql(opts, args))
    self.commit()

Функция create_sql - это фаза 1, и теперь она выражена таким образом, что позволяет вам писать тесты непосредственно против нее: она принимает значения и возвращает значения, поэтому вы можете писать модульные тесты, которые охватывают ее функциональность. Сама функция create теперь стала проще и не нуждается в таком тщательном тестировании: у вас может быть несколько тестов, которые показывают, что она действительно правильно выполняет SQL, но вам не нужно покрывать все крайние случаи в create ,

Кстати: Это видео от Pycon (Тесты и тестируемость) может быть интересным.

6 голосов
/ 20 апреля 2010

Я бы определенно реорганизовал этот метод для простоты тестирования - например, внедрение зависимостей может помочь:

def create(self, opts, args, strtime=None, exec_and_commit=None):
    #I've left out the error handling.
    if strtime is None:
        strtime = datetime.datetime.now().strftime("%D %H:%M")
    vals = (strtime, opts.message, opts.keywords, False)
    if exec_and_commit is None:
        exec_and_commit = self.execute_and_commit
    exec_and_commit("insert into mytable values (?, ?, ?, ?)", vals)

это, конечно, предполагает, что у вас есть метод execute_and_commit, который вызывает execute, а затем commit методы.

Таким образом, тестовый код может внедрить известное значение для strtime и ввести свой собственный вызываемый код как exec_and_commit, чтобы убедиться, что он вызывается с ожидаемыми аргументами.

3 голосов
/ 20 апреля 2010

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

Тем не менее, есть несколько небольших рефакторингов, которые можно выполнить безопасно, без изменения поведения. Переименовать и извлечь два.

Я рекомендую вам взглянуть на книгу Майкла Фезерса Работа с устаревшим кодом . Основное внимание уделяется рефакторингу кода для тестируемости. Примеры даны на Java, но эти концепции также применимы и к Python.

0 голосов
/ 20 апреля 2010

Не знаком с синтаксисом python, но если вы новичок в модульном тестировании, самый простой способ начать - это извлечь метод, который вы передаете в аргументах командной строки, и вернуть команду sql. С помощью этого метода вы можете проверить ту часть кода, в которой лежит настоящая логика. Передайте различные типы аргументов и сравните результаты с тем, какой должна быть команда sql. Как только вы начнете понимать модульное тестирование, вы можете немного узнать о насмешках и внедрении зависимостей, чтобы проверить, правильно ли вы вызываете функции, которые обновляют базу данных.

Надеюсь, вы более знакомы с синтаксисом Java C #, чем я с синтаксисом Python :)

public string GetSqlCommand(string[] commandLineArgs)
{
    //do your parsing here
    return sqlCommand;
}
[Test]
public void emptyArgs_returnsEmptySqlCommand()
{
    string expectedSqlCommand="";
    assert.AreEqual(expectedSqlCommand, GetSqlCommand(new string[])
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...